Как создать массив уникального списка объектов в Swift

Как мы можем создать уникальный список объектов на языке Swift, например NSSet & NSMutableSet в Objective-C.

10 ответов


начиная с Swift 1.2 (Xcode 6.3 beta), Swift имеет собственный тип набора. Из примечаний к выпуску:

новая Set структура данных, которая обеспечивает универсальную коллекция уникальных элементов, с полной семантикой значения. Это мосты с NSSet, обеспечивая функциональность аналогична Array и Dictionary.

вот несколько простых примеров:

// Create set from array literal:
var set = Set([1, 2, 3, 2, 1])

// Add single elements:
set.insert(4)
set.insert(3)

// Add multiple elements:
set.unionInPlace([ 4, 5, 6 ])
// Swift 3: set.formUnion([ 4, 5, 6 ])

// Remove single element:
set.remove(2)

// Remove multiple elements:
set.subtractInPlace([ 6, 7 ])
// Swift 3: set.subtract([ 6, 7 ])

print(set) // [5, 3, 1, 4]

// Test membership:
if set.contains(5) {
    print("yes")
}

но есть гораздо больше методов доступный.

обновление: наборы теперь также задокументированы в "Типы Коллекций" глава документации Swift.


вы можете использовать любой класс Objective-C в Swift:

var set = NSMutableSet()
set.addObject(foo)

Swift не имеет понятия о множествах. Используя NSMutableSet в Swift может быть медленнее, чем с помощью Dictionary, который содержит пустые значения. Вы могли бы сделать это :

var mySet: Dictionary<String, Boolean> = [:]
mySet["something"]= 1

затем просто повторите клавиши.


Я построил обширный Set тип похож на встроенный Array и Dictionary - вот сообщения в блоге один и два и репозиторий GitHub:


extension Array where Element: Hashable {
    var setValue: Set<Element> {
        return Set<Element>(self)
    }
}

let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7]
let uniqueNumbers = numbers.setValue    // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8}

let names = ["John","Mary","Steve","Mary"]
let uniqueNames = names.setValue    // {"John", "Mary", "Steve"}

Я думал, что структура с внутренним словарем будет путь. Я только начал использовать его, поэтому он не завершен, и я еще не знаю о производительности.

struct Set<T : Hashable>
{
    var _items : Dictionary<T, Bool> = [:]

    mutating func add(newItem : T) {
        _items[newItem] = true
    }

    mutating func remove(newItem : T) {
        _items[newItem] = nil
    }

    func contains(item: T) -> Bool {
        if _items.indexForKey(item) != nil { return true } else { return false }
    }

    var items : [T] { get { return [T](_items.keys) } }
    var count : Int { get { return _items.count } }
}

вы действительно можете создать объект Set довольно легко (в отличие от GoZoner, есть встроенный метод contains):

class Set<T : Equatable> {
    var items : T[] = []

    func add(item : T) {
        if !contains(items, { == item}) {
            items += item
        }
    }
}

и вы, возможно, даже хотите объявить пользовательский оператор:

@assignment @infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> {
    for item in items {
        set.add(item)
    }
    return set
}

всегда в таком случае критическим фактором является то, как сравнивать объекты и какие типы объектов входят в набор. Использование Swift-словаря, где объектами Set являются ключи словаря, может быть проблемой, основанной на ограничениях типа ключа (String, Int, Double, Bool, Valueless Enumerations или hashable).

если вы можете определить хэш-функцию на тип объекта, то вы можете использовать словарь. если объекты упорядочиваются, тогда вы можно определить дерево. если объекты сопоставимы только с == затем вам нужно будет выполнить итерацию по заданным элементам, чтобы обнаружить существующий объект.

// When T is only Equatable
class Set<T: Equatable> {
  var items = Array<T>()

  func hasItem (that: T) {
   // No builtin Array method of hasItem... 
   //   because comparison is undefined in builtin Array   
   for this: T in items {
     if (this == that) {
       return true
     }
   }
   return false
  }

  func insert (that: T) {
    if (!hasItem (that))
      items.append (that)
  }
}

выше приведен пример построения Swift Set; в примере используются объекты, которые являются только Equatable - что, хотя и является обычным случаем, не обязательно приводит к эффективному Set реализации (O (N) сложность поиска-пример выше).


поэтому я думаю, что создание набора с массивом-ужасная идея-O (n) - это временная сложность этого набора.

Я собрал хороший набор, который использует словарь:https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift


Я написал функцию для решения этой проблемы.

public func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C {
    var container = C()

    for element in aCollection {
        if !contains(container, element) {
            container.append(element)
        }
    }

    return container
}

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

вы также можете передать Dictionary, String или что-нибудь соответствует ExtensibleCollectionType протокол, если хотите.