从多维数组中删除重复项

Remove duplicates from a multi-dimensional array

我编写了以下扩展程序来从我的数组中删除重复项。

extension Array where Element : Equatable{

    func removeDups() -> [Element]{
        var result = [Element]()

        for element in self{
            if !result.contains(element){
                result.append(element)
            }
        }

        return result
    }
}

线阵

let linearArr = [1,2,2,3]
linearArr.removeDups() // [1,2,3] works well!

多维数组

let multiDimArr : [[Int]] = [[1,2,3], [1,2,3], [1,2 ,4]]
multiDimArr.removeDups() // Error!

Type [Int] does not conform to Equatable

我读了here。答案说使用 == 的数组比较应该有效。它不是一直有效:

有效

if (["1", "2"] == ["1", "2"]){
    print("true")
}

不起作用

if ([1, 2] == [1, 2]){ // ERROR!
    print("true")
}

Ambiguous use of operator '=='

这很奇怪。我可以比较 String 的数组,但不能比较 Int 的数组。

我也看到了this comment

原因myArray1 == myArray2是因为NSObject符合Equatable,调用-[equals:]进行测试

不确定☝️评论是否仍然有效。

总结:

我目前正在与 Swift 4.0.2

合作

这是递归解决问题的地方。你考虑过递归吗?我打算用实际代码回答这个问题,但我不知道 Swift 的语法。所以,这是一些伪代码:

function removeDupes() {
    buffer = array;
    foreach this->elements as key => element {             
         if(element is type array) {
              this->elements[key] = element->removeDupes();
         } else {
              if(!this->contains(element)) {
                  buffer->add(element);
              }
         }             
    }
    return buffer;
}

基本上,您想测试元素本身是否为数组。如果是,那么你想调用该数组的 removeDupes() 方法(它会依次查找重复项,除非它找到另一个数组,然后它会再次调用自己)。

Are arrays equatable? Can I compare them using ==

在 Swift 4.1 之前,Array 不符合 Equatable。然而,== 的重载将两个数组与 Equatable 元素进行比较,这使得它能够编译:

if ["1", "2"] == ["1", "2"] { // using <T : Equatable>(lhs: [T], rhs: [T]) -> Bool
    print("true")
}

但是在 Swift 4.1(适用于 Xcode 9.3)中,当 Element 符合 [=17= 时,Array<Element> 现在符合 Equatable ].此更改is given in the changelog

Swift 4.1

[...]

  • SE-0143 The standard library types Optional, Array, ArraySlice, ContiguousArray, and Dictionary now conform to the Equatable protocol when their element types conform to Equatable. This allows the == operator to compose (e.g., one can compare two values of type [Int : [Int?]?] with ==), as well as use various algorithms defined for Equatable element types, such as index(of:).

您的 multiDimArr.removeDups() 示例按预期在 4.1 中编译和 运行s,产生结果 [[1, 2, 3], [1, 2, 4]]

在 Swift 4.0.3 中,您可以通过为嵌套数组添加另一个 removeDups() 重载来破解它:

extension Array {
  func removeDups<T : Equatable>() -> [Element] where Element == [T] {

    var result = [Element]()

    for element in self{
      if !result.contains(where: { element == [=11=] }) {
        result.append(element)
      }
    }

    return result
  }
}

let multiDimArr = [[1, 2, 3], [1, 2, 3], [1, 2, 4]]
print(multiDimArr.removeDups()) // [[1, 2, 3], [1, 2, 4]]

不幸的是,这确实会导致一些代码重复,但至少您可以在更新到 4.1 时摆脱它。

此示例无法在 4.0.3 或 4.1 中编译的事实:

if [1, 2] == [1, 2] { // error: Ambiguous use of operator '=='
    print("true")
}

是由于错误 SR-5944——由于 ==IndexSetIndexPath 的重载(两者都是 ExpressibleByArrayLiteral).但是 Swift 应该将数组文字默认为 Array,以解决歧义。

说:

if [1, 2] as [Int] == [1, 2] {
    print("true")
}

或不导入 Foundation 可解决问题。


最后,值得注意的是,如果Element类型也是Hashable,可以提高removeDups()的性能,允许它运行线性,而不是比二次时间:

extension Array where Element : Hashable {

  func removeDups() -> [Element] {
    var uniquedElements = Set<Element>()
    return filter { uniquedElements.insert([=14=]).inserted }
  }
}

这里我们使用一个集合来存储我们已经看到的元素,省略了我们已经插入其中的任何元素。这也让我们可以使用 filter(_:), .

in Swift 4.2,Array在其ElementHashable时也有条件地符合Hashable:

Swift 4.2

[...]

  • SE-0143 The standard library types Optional, Array, ArraySlice, ContiguousArray, Dictionary, DictionaryLiteral, Range, and ClosedRange now conform to the Hashable protocol when their element or bound types (as the case may be) conform to Hashable. This makes synthesized Hashable implementations available for types that include stored properties of these types.