Swift map(_:) extension for Set() ?

試著忘記壹切 提交于 2019-11-27 03:17:29

问题


  let numberSet = Set(1...11)
  let divideSet = numberSet.map({  $0 / 10 }) 
  //Error: Set does not have a member named map   :(

Swift 1.2 supports Set() for unordered collections, but map(_:) doesn't seem to work on Sets, so i decide to get smart on my playground and tried:

 let stringSet = Set(map(numberSet, { String($0)}))
 println(stringSet)
 stringSet =  ["2", "11", "1", "8", "6", "4", "3", "9", "7", "10", "5]

This seemed to work. So I tried extending Set:

    extension Set {
        func map<U>(transform: (T) -> U) -> Set<U> {
        return Set(Swift.map(self, transform))  }
    }
   Error: "couldn't find initialiser for Set(T) that accepts argument of type U"

And i think there is a good reason why it doesn’t work, like this example here:

   let smarDividSet = Set(map(numberSet, {$0 / 2})) 
   println(smarDividSet)
   smartDividSet = "[5, 0, 2, 4, 1, 3]” 
  //Somehow elements is the Set are going missing.

Any ideas on how to extend Set to use map(_:) reliably ?. Thank guys.


回答1:


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.




回答2:


The issue with Set.map(_:) is the same with Dictionary.map(_:), and they didn't implement it in the Swift module (standard library) because there's actually no correct way to implement it. The reason is: mapping isn't just enumerating (which you can do with any SequenceType in a for-in), but it's transforming (with the argument closure) each value into another. So you would expect that the result would have the all the transform(element) in it, but guess what, if the values are the same they collapse (for Dictionarys only the keys behave this way).

e.g. (with the proposed implementation) Set([1, 2, 3, 4, 5]).map { 1 }.count == 1

That's also why Swift.map returns an Array and not the same type of SequenceType/CollectionType passed as the source argument.

To digress slightly, Dictionary (as said before) has the same issue on the key values (which is basically a Set), so any map that works either on the Set<K> or the Set<(K, V)> doesn't ensure a consistent mapping, but one that changes the values only would be ok. It wouldn't be a true map though since Dictionarys are collections of tuples, not on value only. So there could be something similar to mapValues which would be correct, but still not a true map on the collection.

TL;DR

map is really useful but be careful on what you're actually doing, because you can't really do a map from Set to Set.


回答3:


As of Swift 2.0 the CollectionType protocol (implemented by Set) has a map method.



来源:https://stackoverflow.com/questions/29107928/swift-map-extension-for-set

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!