Unable to use contains within a Swift Array extension

前端 未结 4 693
故里飘歌
故里飘歌 2020-12-03 15:12

I am trying to write a simple Array extension that provides a \'distinct\' method. Here is what I have so far:

extension Array {
  func distinct() -> T[]          


        
相关标签:
4条回答
  • 2020-12-03 15:34

    Swift 1.x

    The elements in an array don't have to be Equatable, i.e. they don't have be comparable with ==.

    That means you can't write that function for all possible Arrays. And Swift doesn't allow you to extend just a subset of Arrays.

    That means you should write it as a separate function (and that's probably why contains isn't a method, either).

    let array = ["a", "b", "c", "a"]
    
    func distinct<T: Equatable>(array: [T]) -> [T] {
        var rtn = [T]()
    
        for x in array {
            var containsItem = contains(rtn, x)
            if !containsItem {
                rtn.append(x)
            }
        }
        return rtn
    }
    
    distinct(array) // ["a", "b", "c"]
    

    Update for Swift 2/Xcode 7 (Beta)

    Swift 2 supports restricting extensions to a subset of protocol implementations, so the following is now allowed:

    let array = ["a", "b", "c", "a"]
    
    extension SequenceType where Generator.Element: Comparable {
        func distinct() -> [Generator.Element] {
            var rtn: [Generator.Element] = []
    
            for x in self {
                if !rtn.contains(x) {
                    rtn.append(x)
                }
            }
            return rtn
        }
    }
    
    array.distinct() // ["a", "b", "c"]
    

    Note how apple added SequenceType.contains using the same syntax.

    0 讨论(0)
  • 2020-12-03 15:41

    Finally found out how to do it:

    extension Array {
        func contains<T : Equatable>(obj: T) -> Bool {
            return self.filter({$0 as? T == obj}).count > 0
        }
    
        func distinct<T : Equatable>(_: T) -> T[] {
            var rtn = T[]()
    
            for x in self {
                if !rtn.contains(x as T) {
                    rtn += x as T
                }
            }
    
            return rtn
        }
    }
    

    And usage/testing:

    let a = [ 0, 1, 2, 3, 4, 5, 6, 1, 2, 3 ]
    
    a.contains(0)
    a.contains(99)
    
    a.distinct(0)
    

    Unfortunately, I can't figure out a way to do it without having to specify an argument which is subsequently ignored. The only reason it's there is to invoke the correct form of distinct. The major advantage of this approach for distinct seems to be that it's not dumping a common term like distinct into the global namespace. For the contains case it does seem more natural.

    0 讨论(0)
  • 2020-12-03 15:42

    Another solution is to use the find(Array:[T], obj:T) function. It will return an optional Int, so what you could do is

    if let foundResult = find(arr, obj) as Int
    {
         //obj is contained in arr
    } else
    {
         //obj is not contained in arr.
    }
    
    0 讨论(0)
  • 2020-12-03 15:44

    As of Swift 2, this can be achieved with a protocol extension method, e.g. on all types conforming to SequenceType where the sequence elements conform to Equatable:

    extension SequenceType where Generator.Element : Equatable {
    
        func distinct() -> [Generator.Element] {
            var rtn : [Generator.Element] = []
    
            for elem in self {
                if !rtn.contains(elem) {
                    rtn.append(elem)
                }
            }
    
            return rtn
        }
    }
    

    Example:

    let items = [1, 2, 3, 2, 3, 4]
    let unique = items.distinct()
    print(unique) // [1, 2, 3, 4]
    

    If the elements are further restricted to be Hashable then you can take advantage of the Set type:

    extension SequenceType where Generator.Element : Hashable {
    
        func distinct() -> [Generator.Element] {
            return Array(Set(self))
        }
    }
    
    0 讨论(0)
提交回复
热议问题