Comment pouvons-nous créer une liste d'objets unique dans Swift) comme NSSet
& NSMutableSet
dans Objective-C.
À partir de Swift 1.2 (version bêta de Xcode 6.3)), Swift a un type de jeu natif. À partir des notes de publication:
Une nouvelle structure de données
Set
est incluse et fournit une collection générique d’éléments uniques, avec une sémantique de valeur complète. Il établit un pont avecNSSet
et fournit des fonctionnalités analogues àArray
etDictionary
.
Voici quelques exemples d'utilisation simples:
// 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")
}
mais il y a beaucoup plus de méthodes disponibles.
Mise à jour: Les ensembles sont maintenant également décrits dans le chapitre "Types de collection" du Swift = documentation.
Vous pouvez utiliser n’importe quelle classe Objective-C dans Swift:
var set = NSMutableSet()
set.addObject(foo)
Swift n'a pas de concept d'ensembles. Utiliser NSMutableSet
in Swift peut être plus lent que d'utiliser un Dictionary
qui contient des valeurs factices. Vous pouvez le faire:
var mySet: Dictionary<String, Boolean> = [:]
mySet["something"]= 1
Ensuite, il suffit de parcourir les clés.
J'ai construit un type Set
complet similaire au Array
et Dictionary
intégrés. Voici les articles de blog un et deux et un référentiel 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"}
Je pensais qu'une structure avec un dictionnaire interne serait la solution. Je viens tout juste de commencer à l’utiliser, c’est donc incomplet et je n’ai encore aucune idée de la performance.
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 } }
}
En fait, vous pouvez créer un objet Set assez facilement (contrairement à GoZoner, il existe une méthode "contient"):
class Set<T : Equatable> {
var items : T[] = []
func add(item : T) {
if !contains(items, {$0 == item}) {
items += item
}
}
}
et vous voudrez peut-être même déclarer un opérateur personnalisé:
@assignment @infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> {
for item in items {
set.add(item)
}
return set
}
Toujours dans ce cas, le facteur critique est de savoir comment comparer les objets et quels types d’objets vont dans l’ensemble. L'utilisation d'un Swift Dictionary, où les objets Set sont les clés du dictionnaire) peut poser un problème en fonction des restrictions sur le type de clé (String, Int, Double, Bool, Enumerations sans valeur ou hashable).
Si vous pouvez définir une fonction de hachage sur votre type d'objet, vous pouvez utiliser un dictionnaire. Si les objets sont ordonnables, vous pouvez alors définir un arbre. Si les objets ne sont comparables qu'avec ==
alors vous devrez parcourir les éléments de l'ensemble pour détecter un objet préexistant.
// 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)
}
}
Ce qui précède est un exemple de construction d'un Swift Set
; l'exemple utilisé utilise uniquement des objets Equatable
- ce qui, bien que courant, ne conduire à une implémentation efficace de Set
(complexité de la recherche O (N) - l’exemple ci-dessus est un exemple).
Je pense donc que créer un ensemble avec un tableau est une idée terrible - O(n) est la complexité temporelle de cet ensemble.
J'ai mis en place un bel ensemble qui utilise un dictionnaire: https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.Swift
J'ai écrit une fonction pour résoudre ce problème.
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
}
Pour l'utiliser, il suffit de passer un tableau contenant des éléments en double à cette fonction. Et ensuite, il retournera un tableau à l'unicité garantie.
Vous pouvez également passer un Dictionary
, String
ou tout autre élément conforme au protocole ExtensibleCollectionType
si vous le souhaitez.
Cas particulier pour les classes dérivées de NSObject
étant donné que la conformité Equitable (& Hashable) par défaut dans NSObject est fondamentalement une corbeille, vous feriez mieux de vous assurer que vous fournissez une
static func == (lhs: YourClassDerivedFromNSObject, rhs: YourClassDerivedFromNSObject) -> Bool {
mise en œuvre de peur que vous voulez cueillir les doublons insérés dans Set