web-dev-qa-db-fra.com

Existe-t-il un moyen simple de fusionner deux tableaux dans Swift en supprimant les doublons?

Fondamentalement, j'ai besoin d'une version de appendContentsOf: qui n'ajoute pas duplicate elements.

Exemple

var a = [1, 2, 3]
let b = [3, 4, 5]

a.mergeElements(b)
//gives a = [1, 2, 3, 4, 5] //order does not matter
7
prad

Une extension Array peut être créée pour cela. 

extension Array where Element : Equatable{

    public mutating func mergeElements<C : CollectionType where C.Generator.Element == Element>(newElements: C){
       let filteredList = newElements.filter({!self.contains($0)})
       self.appendContentsOf(filteredList)
   }
}

Bien entendu, cela n’est utile que pour les éléments Equatable.

1
prad

Simplement:

let unique = Array(Set(a + b))
18
Oleg Gordiichuk

Ceci est communément appelé union , ce qui est possible dans Swift en utilisant un Set:

let a = [1, 2, 3]
let b = [3, 4, 5]

let set = Set(a)
let union = set.union(b)

Ensuite, vous pouvez simplement convertir l'ensemble en un tableau:

let result = Array(union)
5
Bryan

Swift 3.0 version de la réponse acceptée. 

extension Array where Element : Equatable{

  public mutating func mergeElements<C : Collection>(newElements: C) where C.Generator.Element == Element{
    let filteredList = newElements.filter({!self.contains($0)})
    self.append(contentsOf: filteredList)
  }

}

Note: / Cela vaut la peine de dire ici que le tableau transmis à la fonction est le tableau d'objets qui sera omis du tableau final. Important si vous fusionnez un tableau d'objets dont la propriété Equatable peut être la même, mais d'autres peuvent différer.

3
Kyle Goslan

Swift 4.0 Version

extension Array where Element : Equatable {

  public mutating func mergeElements<C : Collection>(newElements: C) where C.Iterator.Element == Element{
    let filteredList = newElements.filter({!self.contains($0)})
    self.append(contentsOf: filteredList)
  }

}

Comme mentionné: le tableau passé à la fonction est le tableau d'objets qui sera omis du tableau final 

3
Kyle Goslan

J'ai combiné mon extension de Sequence and Array avec this answer pour fournir une syntaxe simple lors de la fusion de tableaux avec des objets personnalisés à l'aide d'une seule propriété:

extension Dictionary {
    init<S>(_ values: S, uniquelyKeyedBy keyPath: KeyPath<S.Element, Key>) where S : Sequence, S.Element == Value {
        let keys = values.map { $0[keyPath: keyPath] }

        self.init(uniqueKeysWithValues: Zip(keys, values))
    }
}

// Unordered example
extension Sequence {
    func merge<T: Sequence, U: Hashable>(mergeWith: T, uniquelyKeyedBy: KeyPath<T.Element, U>) -> [Element] where T.Element == Element {
        let dictOld = Dictionary(self, uniquelyKeyedBy: uniquelyKeyedBy)
        let dictNew = Dictionary(mergeWith, uniquelyKeyedBy: uniquelyKeyedBy)

        return dictNew.merging(dictOld, uniquingKeysWith: { old, new in old }).map { $0.value }
    }
}

// Ordered example
extension Array {
    mutating func mergeWithOrdering<U: Hashable>(mergeWith: Array, uniquelyKeyedBy: KeyPath<Array.Element, U>) {
        let dictNew = Dictionary(mergeWith, uniquelyKeyedBy: uniquelyKeyedBy)

        for (key, value) in dictNew {
            guard let index = firstIndex(where: { $0[keyPath: uniquelyKeyedBy] == key }) else {
                append(value)
                continue
            }

            self[index] = value
        }
    }
}

Tester:

@testable import // Your project name
import XCTest

struct SomeStruct: Hashable {
    let id: Int
    let name: String
}

class MergeTest: XCTestCase {
    let someStruct1 = SomeStruct(id: 1, name: "1")
    let someStruct2 = SomeStruct(id: 2, name: "2")
    let someStruct3 = SomeStruct(id: 2, name: "3")
    let someStruct4 = SomeStruct(id: 4, name: "4")

    var arrayA: [SomeStruct]!
    var arrayB: [SomeStruct]!

    override func setUp() {
        arrayA = [someStruct1, someStruct2]
        arrayB = [someStruct3, someStruct4]
    }

    func testMerging() {
        arrayA = arrayA.merge(mergeWith: arrayB, uniquelyKeyedBy: \.id)

        XCTAssert(arrayA.count == 3)
        XCTAssert(arrayA.contains(someStruct1))
        XCTAssert(arrayA.contains(someStruct3))
        XCTAssert(arrayA.contains(someStruct4))
    }

    func testMergingWithOrdering() {
        arrayA.mergeWithOrdering(mergeWith: arrayB, uniquelyKeyedBy: \.id)

        XCTAssert(arrayA.count == 3)
        XCTAssert(arrayA[0] == someStruct1)
        XCTAssert(arrayA[1] == someStruct3)
        XCTAssert(arrayA[2] == someStruct4)
    }
}
0
J. Doe

Swift 4

func combine(_ sets: Set<String>?...) -> Set<String> {
    return sets.compactMap{$0}.reduce(Set<String>()){$0.union($1)}
}
0
Alexey Chekanov