有没有一种简单的方法在Swift中合并两个数组并同时去除重复元素?

18

基本上,我需要一个 appendContentsOf: 的版本,它不会追加重复的元素。

示例

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个回答

38

简而言之:

let unique = Array(Set(a + b))

1
虽然这很优雅,但只是一个想法:let unique = Array(Set(a + b)).sort() 看起来更好一些... - Dravidian
9
然而,这种方法在将b的元素添加到a的末尾时并不能保持a中元素的顺序,这可能会被认为是不符合预期的行为。由于使用了Set,所以无法维护顺序。 - George Marmaridis
1
你是不是指的是 let unique = Array(Set(a + b)).sorted() - Shaybc

11

使用Set,在Swift中可以轻松进行称为并集的操作:

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

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

然后您可以将这个集合转换为一个数组:

let result = Array(union)

3
尽管想法很清楚,但你的示例无法编译——数字不是字符串。此外,“union”会返回一个新集合,并且不会改变原有集合。 - Martin R
@MartinR 哦,你说得对。这就是我匆忙复制/粘贴的结果,已经修正了。 - Bryan

11

Swift 5 更新

如果您需要组合多个数组。

func combine<T>(_ arrays: Array<T>?...) -> Set<T> {
    return arrays.compactMap{$0}.compactMap{Set($0)}.reduce(Set<T>()){$0.union($1)}
}

使用例子:

1.

    let stringArray1 = ["blue", "red", "green"]
    let stringArray2 = ["white", "blue", "black"]
    
    let combinedStringSet = combine(stringArray1, stringArray2)

    // Result: {"green", "blue", "red", "black", "white"}
    let numericArray1 = [1, 3, 5, 7]
    let numericArray2 = [2, 4, 6, 7, 8]
    let numericArray3 = [2, 9, 6, 10, 8]
    let numericArray4: Array<Int>? = nil
    
    let combinedNumericArray = Array(combine(numericArray1, numericArray2, numericArray3, numericArray4)).sorted()

    // Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5

Swift 4.0版本

(关于it技术)
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)
  }

}

如前所述:传递给函数的数组是将从最终数组中省略的对象数组


3

已接受答案的Swift 3.0版本。

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

}

注意: 这里需要说明的是,传递给函数的数组是最终数组中将被省略的对象数组。如果你要合并一个对象数组,其中Equatable属性可能相同但其他属性可能不同,则这一点非常重要。


1
可以创建一个数组扩展来实现这个功能。
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)
   }
}

当然,这仅对可比较元素有用。

1
我得到了这个错误:'Generator' 不是 'C' 的成员类型。 - Ahmadreza

0

我将 Sequence 和 Array 的扩展与 这个答案 结合起来,以便在仅使用单个属性合并数组时提供易于使用的语法:

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

测试:

@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)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接