Как получить список общих элементов массива 2 в swift
У меня есть два массива:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
Как я могу получить список общих элементов в этих двух массивах, который дает
ouptput = ["mango", "blueberry"]
Я не могу использовать if contains(array, string)
, поскольку я хочу сравнить 2 массива.
Пожалуйста, помогите
Ответы
Ответ 1
Вы также можете использовать 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
vs 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
и short/long String
(от 10 до 100 Character
s) (все генерируемые случайным образом). Я всегда использую 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
для больших типов данных.
Заключение
При малом Array
с примерно 100 элементами подход Array
хорош, но для более крупных нужно использовать подход Set
.
Если вы хотите использовать эти "общие элементы" -операцию несколько раз, желательно использовать Set
только, если это возможно (тип элементов должен быть Hashable
).
Заключительные замечания
Преобразование из Array
в Set
является дорогостоящим, в то время как преобразование с Set
в Array
контрастирует очень недорого.
Использование filter
с .filter(array1.contains)
выполняется быстрее, чем .filter{ array1.contains($0) }
, поскольку:
- последнее создает новое замыкание (только один раз), тогда как первый передает только указатель на функцию
- для последнего вызова замыкания создает дополнительный стек стека, который стоит пространство и время ( несколько раз:
O(N)
)
Ответ 2
Преобразуйте их в 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))
Ответ 3
Вам не нужен набор (как упоминалось выше).
Вместо этого вы можете использовать универсальную функцию, аналогичную используемой 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
}
Эта реализация является уродливой, хотя.
Ответ 4
Swift 3.0
Используйте фильтр для получения общих элементов из двух массивов.
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let newArray = fruitsArray.filter { (string) -> Bool in
return vegArray.contains(string)
}
print(newArray)
// OR
/*let newArray = fruitsArray.filter{vegArray.contains($0)}*/
//Different Element
/*let newArray = fruitsArray.filter{!vegArray.contains($0)}*/
Вывод:
[ "манго", "голубика" ]
Ответ 5
Общий метод, вдохновленный Язык Swift Programming (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
}
Ответ 6
Следующее работает с быстрым 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)
Ответ 7
Вы даже можете сравнивать объекты внутри массива и отфильтровывать общий массив
struct myStruct
{
var userid:String;
var details:String;
init() {
userid = "default value";
details = "default";
}
};
var f1 = myStruct();
f1.userid = "1";
f1.details = "Good boy";
var f2 = myStruct();
f2.userid = "2";
f2.details = "Bad boy";
var f3 = myStruct();
f3.userid = "3";
f3.details = "Gentleman";
var arrNames1:Array = [f1,f3];
var arrNames2:Array = [f3,f1,f2];
let filteredArrayStruct = arrNames1.filter( { (user: myStruct) -> Bool in
return arrNames2.contains({ (user1: myStruct) -> Bool in
return user.userid == user1.userid;
})
})
print(filteredArrayStruct)