web-dev-qa-db-fra.com

Rechercher des éléments en double dans un tableau à l'aide de Swift

Comment trouver des éléments en double dans un tableau? J'ai un tableau de numéros de téléphone donc dans les numéros de téléphone je devrais commencer à chercher du côté droit au côté gauche et trouver 6 nombres entiers similaires. alors je devrais les imprimer.

19
C0mrade

Pour rechercher des doublons, vous pouvez créer une référence croisée par numéro de téléphone, puis filtrer cette liste en doublons uniquement. Par exemple, considérons:

let contacts = [
    Contact(name: "Rob",     phone: "555-1111"),
    Contact(name: "Richard", phone: "555-2222"),
    Contact(name: "Rachel",  phone: "555-1111"),
    Contact(name: "Loren",   phone: "555-2222"),
    Contact(name: "Mary",    phone: "555-3333"),
    Contact(name: "Susie",   phone: "555-2222")
]

Dans Swift 4, vous pouvez créer le dictionnaire de références croisées avec:

let crossReference = Dictionary(grouping: contacts, by: { $0.phone })

Ou

let crossReference = contacts.reduce(into: [String: [Contact]]()) {
    $0[$1.phone, default: []].append($1)
}

Ensuite, pour trouver les doublons:

let duplicates = crossReference
    .filter { $1.count > 1 }                 // filter down to only those with multiple contacts
    .sorted { $0.1.count > $1.1.count }      // if you want, sort in descending order by number of duplicates

Utilisez clairement tous les types de modèle qui vous conviennent, mais ce qui précède utilise le type Contact suivant:

struct Contact {
    let name: String
    let phone: String
}

Il existe de très nombreuses manières d'implémenter cela. Je ne me concentrerai donc pas sur les détails de la mise en œuvre de ce qui précède, mais plutôt sur le concept. juste ces clés avec des valeurs en double.


On dirait que vous voulez aplatir cette structure qui reflète les doublons, en un seul tableau de contacts (je ne sais pas pourquoi vous voudriez le faire, car vous perdez la structure qui identifie les doublons les uns des autres), mais si vous voulez faire cela, vous pouvez flatMap le:

let flattenedDuplicates = crossReference
    .filter { $1.count > 1 }                 // filter down to only those with multiple contacts
    .flatMap { $0.1 }                        // flatten it down to just array of contacts that are duplicates of something else

Pour les rendus Swift 2 ou 3, voir les rendus précédents de cette réponse .

19
Rob

Se sentir ~ malin ~. Étant donné un tableau de Ints

let x = [1, 1, 2, 3, 4, 5, 5]
let duplicates = Array(Set(x.filter({ (i: Int) in x.filter({ $0 == i }).count > 1})))
// [1, 5]

Veuillez noter que ceci est extrêmement efficace pour toutes les personnes impliquées, y compris le compilateur, et vous.

Je montre juste.

Edit: lol quelqu'un a voté contre cela, ce qui m'amène à le répéter, juste au cas où: s'il vous plaît, NE PAS UTILISER CECI en production ou ailleurs.

40
Patrick Perini

Pour filtrer un tableau en fonction de propriétés, vous pouvez utiliser cette méthode:

extension Array {

    func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
        var results = [Element]()

        forEach { (element) in
            let existingElements = results.filter {
                return includeElement(lhs: element, rhs: $0)
            }
            if existingElements.count == 0 {
                results.append(element)
            }
        }

        return results
    }
}

Que vous pouvez appeler comme suit, en vous basant sur l'exemple de contacts de Rob:

let filteredContacts = myContacts.filterDuplicates { $0.name == $1.name && $0.phone == $1.phone }
6
Antoine

Vous pouvez l'implémenter en utilisant "Fusionner le tri" , mais vous devez apporter une modification. Au cours de l'étape de fusion, vous devez ignorer les doublons. 

Le moyen le plus simple de rechercher des éléments en double est de choisir un numéro de téléphone composé de 6 chiffres et de type Int. Vous pouvez trier le tableau de numéros de téléphone et filtrer pour rechercher les doublons.

var phoneNumbers = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]

func findDuplicates(sortedArray array: [Int]) -> [Int]
{
    var duplicates: [Int] = []

    var prevItem: Int = 0
    var addedItem: Int = 0

    for item in array
    {
        if(prevItem == item && addedItem != item)
        {
            duplicates.append(item)
            addedItem = item
        }

        prevItem = item
    }

    return duplicates
}

func sortPhoneNumbers(phoneNumbers: [Int]) -> [Int]
{
    return phoneNumbers.sorted({ return $0<$1 })
}

sortPhoneNumbers(phoneNumbers)
findDuplicates(sortPhoneNumbers(phoneNumbers))

En outre, vous pouvez implémenter la méthode findDuplicates de différentes manières:

Utiliser Set (Swift 1.2+):

func findDuplicates(array: [Int]) -> [Int]
{
    var duplicates = Set<Int>()
    var prevItem = 0       

    for item in array
    {
        if(prevItem == item)
        {
            duplicates.insert(item)
        }

        prevItem = item
    }

    return Array(duplicates)
}

Etc.

5
tikhop

Basé sur la réponse de Rob , une extension de tableau pour trouver uniquement les doublons est:

extension Array where Element: Hashable {
    func duplicates() -> Array {
        let groups = Dictionary(grouping: self, by: {$0})
        let duplicateGroups = groups.filter {$1.count > 1}
        let duplicates = Array(duplicateGroups.keys)
        return duplicates
    }
}
3
Benjohn

La solution d'Antoine in Swift 3+ syntaxe

extension Array {

    func filterDuplicates(includeElement: @escaping (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] {

        var results = [Element]()

        forEach { (element) in

            let existingElements = results.filter {
                return includeElement(element, $0)
            }

            if existingElements.count == 0 {
                results.append(element)
            }
        }
        return results
    }
}
2
Maverick

Comme dans la réponse de @tikhop , mais sous la forme d'une extension Array (Swift 3):

extension Array where Element: Comparable & Hashable {

   public var duplicates: [Element] {

      let sortedElements = sorted { $0 < $1 }
      var duplicatedElements = Set<Element>()

      var previousElement: Element?
      for element in sortedElements {
         if previousElement == element {
            duplicatedElements.insert(element)
         }
         previousElement = element
      }

      return Array(duplicatedElements)
   }

}
1
Vlad

J'ai trouvé un moyen en utilisant réduire, voici le code (Swift 4):

let testNumbers = [1,1,2,3,4,5,2]
let nondupicate = testNumbers.reduce(into: [Int]()) {
    if !$0.contains($1) {
        $0.append($1)
    } else {
        print("Found dupicate: \($1)")
    }
}

En retour, un tableau ne contient aucun élément dupiqué.

Vous pouvez facilement le modifier pour compter les numéros d'éléments dupliqués, vérifier les tableaux de chaînes, etc.

1
Eric Yuan

J'ai également eu un problème similaire et je l'ai surmonté de la manière suivante. (Xcode 8.3.2)

let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var b = a // copy-on-write so that "a" won't be modified

while let c = b.popLast() {
  b.forEach() {
    if $0 == c {
      Swift.print("Duplication: \(c)")
    }
  }
}

//  Duplication: 456789
//  Duplication: 123456

Le point est que le nombre de comparaison. Ce serait plus petit que les autres.

Supposons que le nombre d'éléments du tableau soit N. Dans chaque boucle, le nombre sera décrémenté de un . Ainsi, le nombre total sera égal à (N-1) + (N-2) + (N-3). ) + ... + 2 + 1 = N * (N-1)/2 Lorsque N = 10, ce sera 9 + 8 + ... = 45

En revanche, celui de certains algorithmes pourrait être N * N. Lorsque N = 10, ce sera 100.

En dépit de cela, compte tenu du coût de la copie profonde ou superficielle, je conviens que @ La façon brillante de Patrick Perini serait meilleure que cela dans certaines situations, même le nombre serait de N * N.

MODIFIER:

Manière alternative avec IteratorProtocol

let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var i = a.makeIterator()

while let c = i.next() {
  var j = i
  while let d = j.next() {
    if c == d {
      Swift.print("Duplication: \(c)")
    }
  }
}

//  Duplication: 123456
//  Duplication: 456789

Cela semble plus complexe, mais reprend la même idée qu'auparavant. Cela n'a pas d'allocation de mémoire inutile ni de copie.

Ce qui me préoccupe, c’est l’efficacité, c’est-à-dire une réponse plus rapide de l’interface utilisateur, une plus grande autonomie de la batterie, une empreinte mémoire réduite, etc. Eviter les allocations de mémoire inutiles et/ou les copies de mémoire effectuées automatiquement par Swift serait crucial si nous fournissons des produits compétitifs. (-;

0
Tora

Une réponse très simple qui préserve tous les doublons 

let originalNums = [5, 3, 2, 3 , 7 , 5,3]
var nums = Array(originalNums)

let numSet = Set(nums)

for num in numSet {
  if let index = nums.index(of: num) {
     nums.remove(at: index)
  }
}

sortie

[3, 5, 3]
0
Ryan Heitner