Swift Map(_:) Extension for Set()

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



Leave a reply



Submit