web-dev-qa-db-fra.com

Comment créer un tableau de liste d'objets unique dans Swift

Comment pouvons-nous créer une liste d'objets unique dans Swift) comme NSSet & NSMutableSet dans Objective-C.

54
Subramanian P

À 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 avec NSSet et fournit des fonctionnalités analogues à Array et Dictionary.

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.

51
Martin R

Vous pouvez utiliser n’importe quelle classe Objective-C dans Swift:

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

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.

10

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:

9
Nate Cook
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"}
7
Leo Dabus

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 } }
}
4
Gary Makin

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

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).

2
GoZoner

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

1
EvilPenguin

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.

1
WeZZard

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

0
Anton Tropashko