Как получить список общих элементов массива 2 в Swift?

arrays swift

25361 просмотра

6 ответа

У меня есть два массива:

fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]

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

ouptput = ["mango", "blueberry"]

Я не могу использовать, if contains(array, string)поскольку я хочу сравнить 2 массива.

Автор: AAA Источник Размещён: 12.11.2019 09:52

Ответы (6)


63 плюса

Решение

Вы также можете использовать filterи containsв сочетании:

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]

// only Swift 1
let output = fruitsArray.filter{ contains(vegArray, $0) }

// in Swift 2 and above
let output = fruitsArray.filter{ vegArray.contains($0) }
// or
let output = fruitsArray.filter(vegArray.contains)

Setпротив Arrayодного вычисления общих элементов

Мы рассмотрим следующий фрагмент кода:

let array1: Array = ...
let array2: Array = ...

// `Array`
let commonElements = array1.filter(array2.contains)

// vs `Set`
let commonElements = Array(Set(array1).intersection(Set(array2)))
// or (performance wise equivalent)
let commonElements: Array = Set(array1).filter(Set(array2).contains)

Я сделал несколько (искусственных) тестов с Intи короткими / длинными Stringс (от 10 до 100 Characterс) (все генерируются случайным образом). Я всегда используюarray1.count == array2.count

Я получаю следующие результаты:

Если у вас есть больше, чем critical #(number of) elementsпреобразование в Setявляется предпочтительным

data         |  critical #elements
-------------|--------------------
         Int |        ~50
short String |       ~100
 long String |       ~200

Объяснение результатов

Используя Arrayподход использует «Грубая сила» -Поиск которая имеет временную сложность O(N^2) , где , N = array1.count = array2.countкоторое в отличие от Setподхода O(N). Однако преобразование из Arrayв Setи обратно очень дорого для больших данных, что объясняет увеличение critical #elementsдля больших типов данных.


Заключение

Для маленьких Arrays с приблизительно 100 элементами Arrayподход хорош, но для больших вы должны использовать Setподход.

Если вы хотите использовать эту операцию "общие элементы" несколько раз, рекомендуется использовать Sets только если это возможно (тип элементов должен быть Hashable).

Заключительные замечания

Преобразование из Arrayк Setявляется своего рода дорого в то время как преобразование , Setчтобы Arrayв отличие от очень недорого.

Использование filterwith .filter(array1.contains)повышает быстродействие по сравнению .filter{ array1.contains($0) }с:

  • последний создает новое замыкание ( только один раз ), тогда как первый передает только указатель на функцию
  • для последнего вызова закрытия создает дополнительный кадр стека , который стоит пространство и время ( несколько раз : O(N))
Автор: Qbyte Размещён: 07.09.2015 01:19

20 плюса

Преобразуйте их в Set и используйте функцию intersect ():

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let fruitsSet = Set(fruitsArray)
let vegSet = Set(vegArray)
let output = Array(fruitsSet.intersect(vegSet))
Автор: Mousavian Размещён: 07.09.2015 01:08

13 плюса

Вам не нужен набор (как уже упоминалось в комментариях).

Вместо этого вы можете использовать универсальную функцию, аналогичную той, что Apple использует в своем Swift Tour, и, таким образом, избежать кастинга :

func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool {
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}

Эта функция может принимать любые два массива (SequenceTypes) и, если какой-либо из их элементов одинаков, возвращает true.

Вы можете просто изменить эту универсальную функцию, чтобы упаковать массив строк и вернуть его вместо этого.

Например, вот так:

func arrayOfCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
    var returnArray:[T.Generator.Element] = []
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                returnArray.append(lhsItem)
            }
        }
    }
    return returnArray
}

Использование как это:

var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]


var result = arrayOfCommonElements(one,other)

print(result) //prints [test2, dog, cat]

Дополнительным преимуществом здесь является то, что эта функция также работает со всеми однотипными массивами. Итак, позже, если вам нужно сравнить два [myCustomObject]массива, как только они оба станут равными, у вас все готово ! (каламбур предназначен)

Редактировать: (для не общих элементов) вы можете сделать что-то вроде этого

func arrayOfNonCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {

    var returnArray:[T.Generator.Element] = []
    var found = false

    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                found = true
                break
            }
        }

        if (!found){
            returnArray.append(lhsItem)
        }

        found = false
    }
    for rhsItem in rhs {
        for lhsItem in lhs {
            if rhsItem == lhsItem {
                found = true
                break
            }
        }

        if (!found){
            returnArray.append(rhsItem)
        }

        found = false
    }
    return returnArray
}

Эта реализация уродлива, хотя.

Автор: Woodstock Размещён: 07.09.2015 01:06

4 плюса

Следующие работы со Swift 4:

   let fruitsArray = ["apple", "mango", "blueberry", "orange"]
   let vegArray = ["tomato", "potato", "mango", "blueberry"]

   var someHash: [String: Bool] = [:]

   fruitsArray.forEach { someHash[$0] = true }

   var commonItems = [String]()

   vegArray.forEach { veg in
    if someHash[veg] ?? false {
        commonItems.append(veg)
    }
   }

   print(commonItems)
Автор: Mehul Parmar Размещён: 08.01.2018 10:03

3 плюса

Общий метод, основанный на упражнении Swift Programming Language (Swift 3) :

func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        var common: [T.Iterator.Element] = []

        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    common.append(lhsItem)
                }
            }
        }
        return common
}

Затем используйте это так:

var a = [3,88,74]
var b = [1,3,88]

print("commons: \(commonElements(a, b))")

--> commons: [3, 88]
Автор: Nicolas Buquet Размещён: 16.09.2016 09:59

0 плюса

Используя Set, а также пересечение следующим образом:

func findIntersection (firstArray : [Int], secondArray : [Int]) -> [Int]
{
    return [Int](Set<Int>(firstArray).intersection(secondArray))
}

print (findIntersection(firstArray: [2,3,4,5], secondArray: [1,2,3]))
Автор: casillas Размещён: 20.10.2018 01:44
Вопросы из категории :
32x32