Невозможно использовать в расширении Swift Array

Я пытаюсь написать простое расширение Array, которое предоставляет "отличный" метод. Вот что я до сих пор:

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

    for x in self {
      var containsItem = contains(rtn, x)
      if !containsItem {
        rtn.append(x)
      }
    }
    return rtn
  }
 }

Проблема заключается в том, что инструкция 'contains' не работает следующим образом:

Не удалось найти перегрузку для 'contains', которая принимает предоставленные аргументы

Я уверен, что ограничения типа верны. Любые идеи?

Ответ 1

Swift 1.x

Элементы в array не должны быть Equatable, то есть они не сравнимы с ==.

Это означает, что вы не можете записать эту функцию для всех возможных массивов. И Swift не позволяет вам распространять только подмножество массивов.

Это означает, что вы должны написать его как отдельную функцию (и, вероятно, почему contains тоже не является методом).

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"]

Обновление для Swift 2/Xcode 7 (бета-версия)

Swift 2 поддерживает ограничение расширений подмножеством реализаций протокола, поэтому теперь разрешено следующее:

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"]

Обратите внимание, как добавлено приложение apple SequenceType.contains с использованием того же синтаксиса.

Ответ 2

Наконец выяснилось, как это сделать:

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
    }
}

И использование/тестирование:

let a = [ 0, 1, 2, 3, 4, 5, 6, 1, 2, 3 ]

a.contains(0)
a.contains(99)

a.distinct(0)

К сожалению, я не могу найти способ сделать это, не указывая аргумент, который впоследствии игнорируется. Единственная причина, по которой он должен вызывать правильную форму отдельного. Основным преимуществом такого подхода для distinct является то, что он не демпинг общего термина типа distinct в глобальное пространство имен. Для случая сложения это кажется более естественным.

Ответ 3

Другим решением является использование функции find (Array: [T], obj: T). Он вернет необязательный Int, поэтому вы можете сделать

if let foundResult = find(arr, obj) as Int
{
     //obj is contained in arr
} else
{
     //obj is not contained in arr.
}

Ответ 4

С Swift 2 это может быть достигнуто с помощью метода расширения протокола, например на всех типах, соответствующих SequenceType, где последовательность элементы соответствуют 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
    }
}

Пример:

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

Если элементы далее ограничены Hashable, тогда вы может использовать тип Set:

extension SequenceType where Generator.Element : Hashable {

    func distinct() -> [Generator.Element] {
        return Array(Set(self))
    }
}