【问题标题】:Find Duplicate Elements In Array Using Swift使用 Swift 查找数组中的重复元素
【发布时间】:2015-04-19 08:32:03
【问题描述】:

如何查找数组中的重复元素?我有电话号码数组,所以在电话号码中我应该从右侧到左侧开始搜索并找到类似的 6 个整数。那我应该把它们打印出来。

【问题讨论】:

  • 为什么要从 cellForRow 调用你的函数...正确的做法是先找到重复项,然后显示这些...
  • 用这个函数我应该把所有的联系信息保存在数组中,然后在这个数组中执行搜索是吗?之后我将获得一个包含重复项目的新数组,并且在单元格中显示它会很简单吗?我说的对吗?
  • 您的问题过于宽泛,混合了两个不同(不相关)的问题:1)如何在通讯录中查找“重复联系人”。 2) 如何在表格视图中显示结果。 – 我建议您将问题限制在一个问题上。如果解决了,您可以在必要时发布另一个问题。
  • 我已更改问题并发布了单个问题。

标签: arrays swift ios8 addressbook


【解决方案1】:

感觉〜聪明〜。给定一个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]

请注意,这对于所有相关人员(包括编译器和您)来说效率都非常低。

我只是在炫耀。

编辑:大声笑有人对此表示反对,这让我重申,以防万一:请不要在生产或其他任何地方使用它。

【讨论】:

  • 确实聪明!!
  • 你知道我必须遵守什么协议才能实现这个方法吗?
  • 我喜欢这个单线!呜呜呜!??
  • Answer 按 Aspected 工作。但这不是内存高效的解决方案。如果你在 X-code Playground 中运行代码,那一行将运行 57 次。因此,如果您专注于解决大量数据。在这种情况下,我不会应用此解决方案。
  • 不错的答案。在 Swift 4 中,您可以将 (i: Int) 替换为 i 并让编译器处理类型。
【解决方案2】:

要查找重复项,您可以按电话号码建立交叉引用,然后将其过滤为仅重复项。例如,考虑:

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")
]

在 Swift 4 中,您可以使用以下方法构建交叉引用字典:

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

或者,在 Swift 5.2 中(感谢SE-0249),您可以这样做:

let crossReference = Dictionary(grouping: contacts, by: \.phone)

或者

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

然后,查找重复项:

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

显然使用对您有意义的任何模型类型,但上面使用以下Contact 类型:

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

有很多很多的实现方式,所以我不会关注上面的实现细节,而是关注概念:通过某个键(例如电话号码)构建交叉引用原始数组,然后过滤结果只是那些具有重复值的键。


听起来您想将这种反映重复的结构展平为单个联系人数组(我不确定您为什么要这样做,因为您丢失了识别哪些是彼此重复的结构),但如果你想这样做,你可以flatMap它:

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

对于 Swift 2 或 3 版本,请参阅 previous renditions of this answer

【讨论】:

  • 谢谢。所以我按照你说的做了一切,但我不明白为什么它在控制台上给了我100次重复?
  • 这让我想知道原始数组是什么样子的。 (您的其他问题之一是您在cellForRowAtIndexPath 内的数组中添加行。)仔细检查原始数组(例如查看count 的总数)并确保输入正确。
  • 抱歉,只有一个问题,我已经纠正了我的错误,现在我的数字和姓名计数正确,但如何解决这个问题:[(555-1111,[姓名:Rob - 电话 555-1111,姓名:雷切尔 - 电话 555-1111])]。正如我输入的那样:姓名:Rob - 电话:555-1111,姓名 Rachel - 电话 555-1111 ...
  • 如何显示结果完全取决于您。我假设您希望在表格视图中为每组重复项设置一个单独的部分,然后该部分中的每一行都是具有相同电话号码的联系人的姓名。我想象带有电话号码的标题,然后是共享该电话号码的联系人的行。如果是这样,我在这里使用的模型可能更有利于该输出。如果你真的想要一个所有重复的部分一起运行,我想你可以这样做,但它让我觉得不那么优雅。
  • 谢谢 Rob,这对您很有帮助!
【解决方案3】:

斯威夫特 4+

2行,快速解决:

var numbers = [1,2,3,4,5,6,6,6,7,8,8]
let dups = Dictionary(grouping: numbers, by: {$0}).filter { $1.count > 1 }.keys

//Results: [6, 8]

【讨论】:

  • 如果您正在使用可识别数据,则对此很有用:` func duplicateIds() -> Dictionary.Keys { Dictionary(grouping: self , by: { $0.id }) .filter { $1.count > 1 } .keys } `
【解决方案4】:

完全源自Rob's very neat answer。为了清楚起见,我已将其添加到 Array 扩展中,并为中间步骤命名:

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

[1, 2, 2, 3, 1].duplicates() -> [1, 2]

【讨论】:

    【解决方案5】:

    您可以使用"Merge sort" 实现它,但是您需要进行一项修改,在合并步骤中您应该忽略重复项。

    查找重复元素的最简单方法是,如果电话号码只是一个 6 位数字并且类型为 Int,则可以对电话号码数组进行排序,然后过滤以查找重复项。

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

    此外,您可以通过不同的方式实现 findDuplicates 方法:

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

    等等。

    【讨论】:

    • @Mazyod - 来自答案:“您可以对电话号码数组进行排序,然后过滤以查找重复项。”
    • 1.该函数应该这样做,因为名称并不意味着输入已排序。 2. 仔细看,你在每次迭代中将prevItem 重置为0
    • 如果函数名是findDuplicates(sortedArray array: [Int]) -&gt; [Int],那将是有意义的。但是读取当前名称的开发人员不会意识到您传入的数组必须是排序的。
    • @Mazyod 如果开发人员从头到尾阅读答案,这也是有意义的。我提供了有助于解决问题的代码 sn-p。就是这样。此外,如果您发现拼写错误,您可以修复它(如 2.)
    【解决方案6】:

    要根据属性过滤数组,可以使用这种方法:

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

    根据 Rob 的联系人示例,您可以如下调用:

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

    【讨论】:

      【解决方案7】:

      我找到了一种使用reduce的方法,这里是代码(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 duplicate: \($1)")
          }
      }
      

      作为副作用,它返回一个没有重复元素的数组。

      您可以轻松修改它以计算重复元素数量、检查字符串数组等。

      【讨论】:

        【解决方案8】:

        Antoine's solutionSwift 3+ 语法中

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

        【讨论】:

          【解决方案9】:

          @tikhop 的答案相同,但作为数组扩展(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)
             }
          
          }
          

          【讨论】:

            【解决方案10】:
            let inputArray = [9820213496, 9546533545, 9820213496, 995543567]
            var outputArray = [Int]()
            for element in inputArray{
                if outputArray.contains(element){
                    print("\(element) is Duplicate")
                }else{
                    outputArray.append(element)
                }
            }
            print(outputArray) // print Array without duplication
            

            【讨论】:

              【解决方案11】:

              一个非常简单的答案,保留所有重复项

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

              输出

              [3, 5, 3]
              

              【讨论】:

                【解决方案12】:

                我也有类似的问题,并通过以下方式克服。 (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
                

                重点是比较的数量。它会比其他的更小。

                假设数组中的项目数为 N。在每个循环中,数字将减一。 因此,总数将为 (N-1) + (N-2) + (N-3) + ... + 2 + 1 = N * (N-1) / 2 当 N = 10 时,将是 9 + 8 + ... = 45

                相比之下,某些算法可能是 N * N。当 N = 10 时,将是 100。

                尽管如此,考虑到深拷贝或浅拷贝的成本,我同意 @Patrick Perini 的绝妙方式在某些情况下会比这更好,即使数量是 N * N。

                编辑:

                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
                

                这看起来更复杂,但使用与以前相同的想法。这没有不必要的内存分配或副本。

                我关心的是效率,即更快的 UI 响应、更长的电池寿命、更小的内存占用等。如果我们要提供有竞争力的产品,避免 Swift 在后台自动完成的不必要的内存分配和/或内存复制将是至关重要的产品。 (-;

                【讨论】:

                  【解决方案13】:
                  extension Array where Element: Hashable {
                       func similar() -> Self {
                          var used = [Element: Bool]()
                  
                          return self.filter { used.updateValue(true, forKey: $0) != nil }
                      }
                  }
                  

                  【讨论】:

                  • 您能否更新您的答案以包括对您正在做什么以及为什么它与其他现有答案不同的解释?对于 Stack Overflow 的答案,这始终是一个好主意,但对于这些有很多已确定答案的老问题,这一点尤其重要。
                  【解决方案14】:
                  // find duplicate number in an array 
                  var arrNum = [1, 2, 3 , 3, 2, 5, 6, 2] 
                  let setOfNum = Set(Array(arrNum))
                  print(setOfNum)
                  
                  Output: [6, 3, 5, 1, 2]
                  
                  // find duplicate string in an array 
                  var arrStr = ["1", "2", "3" , "3", "2", "5", "6", "2"]  
                  let setOfStr = Set(Array(arrStr))
                  print(setOfNum)
                  
                  Output: [6, 3, 5, 1, 2]
                  

                  【讨论】:

                    【解决方案15】:

                    这是一种高效的 O(n) 方法。这里的一些其他答案在duplicates 数组甚至返回值数组上使用.filter,这使得操作在O(n^2) 中工作(使用.contains 是相同的)。使用Set 来存储重复项,我们可以将其设为 O(n)。

                    这里展示的另一种方法是使用字典首先存储数组元素。这个想法是字典不能有重复的元素。但是,这并不能保证保留数组的原始顺序,因此我们需要另一种方法。

                    这是一个 Array 扩展,它添加了一个高效的 removeDuplicates 方法并保证与原始数组的顺序相同的结果顺序。

                    extension Array where Iterator.Element == Int {
                        func removeDuplicates() -> [Int] {
                            var retVal: [Int] = []
                            var duplicates: Set<Int> = []
                            
                            for number in self {
                                if !duplicates.contains(number) {
                                    duplicates.insert(number)
                                    retVal.append(number)
                                }
                            }
                            
                            return retVal
                        }
                    }
                    

                    如果要返回重复的元素,只需反转 for 循环中的一些检查即可(仍然是 O(n))。

                    extension Array where Iterator.Element == Int {
                        func findDuplicates() -> [Int] {
                            var retVal: [Int] = []
                            var duplicates: Set<Int> = []
                            
                            for number in self {
                                if duplicates.contains(number) {
                                    retVal.append(number)
                                } else {
                                    duplicates.insert(number)
                                }
                            }
                            
                            return retVal
                        }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2018-02-02
                      • 1970-01-01
                      • 2011-06-06
                      • 2017-08-18
                      • 1970-01-01
                      • 2021-01-14
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多