Swift map(_:) extension for Set() ?
Update: Quite a lot changed with Swift 2 and 3. The generic
placeholder of Set
is now Element
instead of T
, and all
collections have a map()
method which returns an array.
There were also good arguments given about the problems of a Set -> Set
mapping (such as different elements mapping to the same result).
On the other hand, there may be a use-case for such a mapping,
so here is an update for Swift 3 (now using a different name).
extension Set {
func setmap<U>(transform: (Element) -> U) -> Set<U> {
return Set<U>(self.lazy.map(transform))
}
}
Example:
let numberSet = Set(1...11)
let divideSet = numberSet.setmap { $0 / 2 }
print(divideSet) // [5, 0, 2, 4, 1, 3]
(Old answer:) You were almost there. For some reason, the generic type of the
returned set must be specified explicitly:
extension Set {
func map<U>(transform: (T) -> U) -> Set<U> {
return Set<U>(Swift.map(self, transform))
}
}
Example:
let numberSet = Set(1...11)
let divideSet = numberSet.map { $0 / 2 }
println(divideSet) // [5, 0, 2, 4, 1, 3]
The resulting set has less elements because the integer division$0 / 2
truncates the quotient, e.g. both 4/2 and 5/2 map to
the same element 2. This does not happen with floating point division:
let floatdivideSet = numberSet.map { Double($0) / 2.0 }
println(floatdivideSet) // [4.0, 5.0, 4.5, 5.5, 2.0, 3.0, 3.5, 2.5, 1.5, 1.0, 0.5]
Another possible implementation is
extension Set {
func map<U>(transform: (T) -> U) -> Set<U> {
return Set<U>(lazy(self).map(transform))
}
}
Here lazy(self)
returns a LazyForwardCollection
which has
a map()
method and that returns a LazyForwardCollection
again.
The advantage might be that no intermediate array is created.
How to use a map method on Set T ?
The problem is that set.map
(really CollectionType.map
) returns an Array
, not a Set
. Hence:
let res = set.map { $0 + "_bar" }
Works and leaves you with [String]
which you can convert back to a Set<String>
with the constructor:
let res : Set<String> = Set(set.map{$0 + "_bar"})
Iterate and modify Struct in a Set
You can assign mySet
as mapped mySet
converted to Set
mySet = Set(mySet.map { $0 + 1 })
Reduce array to set in Swift
You don't have to reduce an array to get it into a set; just create the set with an array: let objectSet = Set(objects.map { $0.URL })
.
How can I extend typed Arrays in Swift?
For extending typed arrays with classes, the below works for me (Swift 2.2). For example, sorting a typed array:
class HighScoreEntry {
let score:Int
}
extension Array where Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0.score < $1.score }
}
}
Trying to do this with a struct or typealias will give an error:
Type 'Element' constrained to a non-protocol type 'HighScoreEntry'
Update:
To extend typed arrays with non-classes use the following approach:
typealias HighScoreEntry = (Int)
extension SequenceType where Generator.Element == HighScoreEntry {
func sort() -> [HighScoreEntry] {
return sort { $0 < $1 }
}
}
In Swift 3 some types have been renamed:
extension Sequence where Iterator.Element == HighScoreEntry
{
// ...
}
Swift: Extension on [ SomeType T ?] to produce [ T ?] possible?
I don't know if there is a simpler solution now, but you can use the same “trick” as in How can I write a function that will unwrap a generic property in swift assuming it is an optional type? and Creating an extension to filter nils from an Array in Swift, the idea goes back to this Apple Forum Thread.
First define a protocol to which all optionals conform:
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional: Wrapped? {
return self
}
}
Now the desired extension can be defined as
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map( { $0.asOptional?.value })
}
}
and that works as expected:
let arr = [SomeType(value: 123), nil, SomeType(value: 456)]
let v = arr.values
print(v) // [Optional(123), Optional(456)]
print(type(of: v)) // Array<Optional<Int>>
Swift Set to Array
You can create an array with all elements from a given Swift Set
simply with
let array = Array(someSet)
This works because Set
conforms to the SequenceType
protocol
and an Array
can be initialized with a sequence. Example:
let mySet = Set(["a", "b", "a"]) // Set<String>
let myArray = Array(mySet) // Array<String>
print(myArray) // [b, a]
What's the cleanest way of applying map() to a dictionary in Swift?
Swift 4+
Good news! Swift 4 includes a mapValues(_:)
method which constructs a copy of a dictionary with the same keys, but different values. It also includes a filter(_:)
overload which returns a Dictionary
, and init(uniqueKeysWithValues:)
and init(_:uniquingKeysWith:)
initializers to create a Dictionary
from an arbitrary sequence of tuples. That means that, if you want to change both the keys and values, you can say something like:
let newDict = Dictionary(uniqueKeysWithValues:
oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
There are also new APIs for merging dictionaries together, substituting a default value for missing elements, grouping values (converting a collection into a dictionary of arrays, keyed by the result of mapping the collection over some function), and more.
During discussion of the proposal, SE-0165, that introduced these features, I brought up this Stack Overflow answer several times, and I think the sheer number of upvotes helped demonstrate the demand. So thanks for your help making Swift better!
Related Topics
Value of Optional Type Cgfloat Not Unwrapped Error in Swift
How to Test Whether Generic Variable Is of Type Anyobject
How to Make Physics Bodies Stick to Nodes Anchor Points
Division Not Working Properly in Swift
What Are 'Get' and 'Set' in Swift
How to Declare an Inline Function in Swift
Differencebetween Http Parameters and Http Headers
Swift Setting Badge Value for Uitabbaritem
Swiftui: Prevent Image() from Expanding View Rect Outside of Screen Bounds
How to Disable Vertical Scroll in Tabview with Swiftui
Make Property of Type and Also Conform to Protocol in Swift
Invalid Update: Invalid Number of Items in Section 0
How to Write a Function That Will Unwrap a Generic Property in Swift Assuming It Is an Optional Type
Difference Between "Precondition" and "Assert" in Swift
Swift's JSONdecoder with Multiple Date Formats in a JSON String