Поиск Повторяющихся Элементов В Массиве С Помощью Swift

Как найти повторяющиеся элементы в массиве? У меня есть массив телефонных номеров, поэтому в телефонных номерах я должен начать поиск с правой стороны на левую сторону и найти похожие 6 целых чисел. тогда я их распечатаю.

10 ответов


чтобы найти дубликаты, вы можете создать перекрестную ссылку по номеру телефона, а затем отфильтровать ее только до дубликатов. Например, рассмотрим:

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: { .phone })

или

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

затем, чтобы найти дубликаты:

let duplicates = crossReference
    .filter { .count > 1 }                 // filter down to only those with multiple contacts
    .sorted { .1.count > .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 { .count > 1 }                 // filter down to only those with multiple contacts
    .flatMap { .1 }                        // flatten it down to just array of contacts that are duplicates of something else

для Swift 2 или 3 версии, см. предыдущие версии этого ответа.


чувство ~умная~. Учитывая массив Ints

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

обратите внимание, что это ужасно эффективно для всех участников, включая компилятор и вас.

Я просто выпендриваюсь.

Edit: lol кто-то понизил это, что заставляет меня повторить, на всякий случай: пожалуйста, не используйте это в производстве или где-либо еще.


для фильтрации массива на основе свойств можно использовать следующий метод:

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: )
            }
            if existingElements.count == 0 {
                results.append(element)
            }
        }

        return results
    }
}

который вы можете вызвать следующим образом, на основе примера контактов Rob:

let filteredContacts = myContacts.filterDuplicates { .name == .name && .phone == .phone }

вы можете реализовать его с помощью "сортировка слиянием", но вам нужно сделать одну модификацию, во время шага слияния вы должны игнорировать дубликаты.

самый простой способ найти дубликаты элементов, если номер телефона просто 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 < })
}

sortPhoneNumbers(phoneNumbers)
findDuplicates(sortPhoneNumbers(phoneNumbers))

кроме того, вы можете реализовать метод findDuplicates в разных пути:

Использование Набора (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)
}

и так далее.


то же, что и в @tikhopответ, но как расширение массива (Swift 3):

extension Array where Element: Comparable & Hashable {

   public var duplicates: [Element] {

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

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

      return Array(duplicatedElements)
   }

}

Я нашел способ с помощью reduce, вот код (Swift 4):

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

как побочный эффект, он возвращает массив имеет dupicated элементов.

вы можете легко изменить его для подсчета числа дублированных элементов, проверка строковых массивов и т. д.


Антуана на Swift 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, )
            }

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

на основе Роб расширение массива просто находит дубликаты:

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

очень простой ответ, который сохраняет все дубликаты

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]

у меня также была аналогичная проблема и я преодолел ее следующим образом. (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  == c {
      Swift.print("Duplication: \(c)")
    }
  }
}

//  Duplication: 456789
//  Duplication: 123456

дело в том, что количество сравнений. Он будет меньше других.

предположим, что количество элементов в массиве равно N. В каждом цикле число будет уменьшаться на единицу. Таким образом, общее число будет (N-1) + (п-2) + (н-3) + ... + 2 + 1 = N * (N-1) / 2 Когда N = 10, это будет 9 + 8 + ... = 45

В отличие от некоторых алгоритмов может быть N * N. Когда N = 10, это будет 100.

несмотря на это, принимая во внимание стоимость глубокой копии или мелкой копии, я согласен с тем, что @ Блестящий путь Патрика Перини был бы лучше, чем это в некоторых ситуациях, даже число которых было бы N * N.

EDIT:

альтернативный способ с 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, более длиннее время работы от батарей, более малый след ноги памяти, etc. Во избежание ненужных распределений памяти и / или копий памяти, которые автоматически выполняются Swift за кулисами, было бы важно, если мы предоставляем конкурентоспособные продукты. (-;