I have two arrays:
我有两个数组:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
How can I get the list of common items in those two array which gives
我如何得到这两个数组中的公共项的列表
ouptput = ["mango", "blueberry"]
I can't use if contains(array, string)
as I want to compare 2 arrays.
我不能使用if包含(数组,字符串),因为我想比较两个数组。
7 个解决方案
#1
40
You can also use filter
and contains
in conjunction:
你也可以使用过滤器,并结合:
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
for a single computation of common elements
We consider the following code snippet:
我们考虑以下代码片段:
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)
I have made some (artificial) benchmarks with Int
and short/long String
s (10 to 100 Character
s) (all randomly generated). I always use array1.count == array2.count
我用Int和short/long字符串(10到100个字符)(都是随机生成的)做了一些(人工)基准测试。我总是使用array1。数= = array2.count
I get the following results:
我得到以下结果:
If you have more than critical #(number of) elements
converting to a Set
is preferable
如果您有多个关键的#(元素的数量)元素转换到一个集合是最好的
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explanation of the results
Using the Array
approach uses "Brute force"-search which has time complexity O(N^2)
where N = array1.count = array2.count
which is in contrast to the Set
approach O(N)
. However the conversion from Array
to Set
and back is very expensive for large data which explains the increase of critical #elements
for bigger data types.
使用数组方法使用“蛮力”搜索的时间复杂度O(N ^ 2)N = array1。数= array2。计数与集合方法O(N)相反。然而,从数组到Set和back的转换对于大数据来说是非常昂贵的,这就解释了为什么大数据类型的关键#元素会增加。
Conclusion
For small Array
s with about 100 elements the Array
approach is fine but for larger ones you should use the Set
approach.
对于包含大约100个元素的小数组,数组方法是可以的,但是对于较大的数组,应该使用Set方法。
If you want to use this "common elements"-operation multiple times it is advisable to use Set
s only if possible (the type of the elements has to be Hashable
).
如果您想要使用这个“公共元素”——多次操作,建议只在可能的情况下使用集合(元素的类型必须是可清洗的)。
Final Remarks
A conversion from Array
to Set
is kind of expensive while the conversion from Set
to Array
is in contrast very inexpensive.
从数组到集合的转换比较昂贵,而从集合到数组的转换比较便宜。
Using filter
with .filter(array1.contains)
is performance wise faster than .filter{ array1.contains($0) }
since:
使用带有.filter(array1.contains)的filter比.filter{array1.contains($0)}更快的性能,因为:
- the last one creates a new closure (only once) whereas the first one passes only a function pointer
- 最后一个只创建一个新闭包(只有一次),而第一个只传递一个函数指针
- for the last one the call of the closure creates an additional stack frame which costs space and time (multiple times:
O(N)
) - 对于最后一个,闭包的调用创建了一个额外的堆栈框架,它需要花费空间和时间(多次:O(N)))
#2
16
Convert them to Set and use intersect() function:
将它们转换为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
12
You don't need a Set (as the comments above have mentioned).
您不需要一个集合(正如上面的注释所提到的)。
You could instead use a generic function, similar to the one Apple use in their Swift Tour, and thus avoid casting:
相反,你可以使用一个通用的功能,类似于苹果在他们的快速旅行中使用的功能,从而避免选择:
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
}
This function can take any two arrays (SequenceTypes) and if any of their elements are the same it returns true.
这个函数可以取任意两个数组(SequenceTypes),如果它们中的任何一个元素都相同,则返回true。
You could simply modify this generic function to package up an array of strings and return that instead.
您可以简单地修改这个通用函数来打包字符串数组并返回它。
For example like this:
例如像这样:
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
}
Usage like this:
用法如下:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
The added benefit here is that this function also works with all same typed arrays. So later if you need to compare two [myCustomObject]
arrays, once they both conform to equatable, you're all set! (pun intended)
这里的附加好处是,这个函数也适用于所有类型化数组。因此,如果以后需要比较两个[myCustomObject]数组,一旦它们都符合equatable,就都设置好了!(一语双关)
Edit: (For non common elements) you could do something like this
编辑:(对于非公共元素)你可以这样做
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
}
This implementation is ugly though.
然而,这个实现是丑陋的。
#4
2
Swift 3.0
斯威夫特3.0
Use filter to get common elements from two arrays.
使用过滤器从两个数组中获取公共元素。
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)}*/
Output:
输出:
["mango", "blueberry"]
(“芒果”,“蓝莓”)
#5
2
The following works with swift 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)
#6
1
A generic method, inspired by The Swift Programming Language (Swift 3) exercise:
受Swift编程语言(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
}
#7
-2
You can even compare the objects inside the array and filter out common array
您甚至可以比较数组中的对象并过滤掉公共数组
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)
#1
40
You can also use filter
and contains
in conjunction:
你也可以使用过滤器,并结合:
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
for a single computation of common elements
We consider the following code snippet:
我们考虑以下代码片段:
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)
I have made some (artificial) benchmarks with Int
and short/long String
s (10 to 100 Character
s) (all randomly generated). I always use array1.count == array2.count
我用Int和short/long字符串(10到100个字符)(都是随机生成的)做了一些(人工)基准测试。我总是使用array1。数= = array2.count
I get the following results:
我得到以下结果:
If you have more than critical #(number of) elements
converting to a Set
is preferable
如果您有多个关键的#(元素的数量)元素转换到一个集合是最好的
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explanation of the results
Using the Array
approach uses "Brute force"-search which has time complexity O(N^2)
where N = array1.count = array2.count
which is in contrast to the Set
approach O(N)
. However the conversion from Array
to Set
and back is very expensive for large data which explains the increase of critical #elements
for bigger data types.
使用数组方法使用“蛮力”搜索的时间复杂度O(N ^ 2)N = array1。数= array2。计数与集合方法O(N)相反。然而,从数组到Set和back的转换对于大数据来说是非常昂贵的,这就解释了为什么大数据类型的关键#元素会增加。
Conclusion
For small Array
s with about 100 elements the Array
approach is fine but for larger ones you should use the Set
approach.
对于包含大约100个元素的小数组,数组方法是可以的,但是对于较大的数组,应该使用Set方法。
If you want to use this "common elements"-operation multiple times it is advisable to use Set
s only if possible (the type of the elements has to be Hashable
).
如果您想要使用这个“公共元素”——多次操作,建议只在可能的情况下使用集合(元素的类型必须是可清洗的)。
Final Remarks
A conversion from Array
to Set
is kind of expensive while the conversion from Set
to Array
is in contrast very inexpensive.
从数组到集合的转换比较昂贵,而从集合到数组的转换比较便宜。
Using filter
with .filter(array1.contains)
is performance wise faster than .filter{ array1.contains($0) }
since:
使用带有.filter(array1.contains)的filter比.filter{array1.contains($0)}更快的性能,因为:
- the last one creates a new closure (only once) whereas the first one passes only a function pointer
- 最后一个只创建一个新闭包(只有一次),而第一个只传递一个函数指针
- for the last one the call of the closure creates an additional stack frame which costs space and time (multiple times:
O(N)
) - 对于最后一个,闭包的调用创建了一个额外的堆栈框架,它需要花费空间和时间(多次:O(N)))
#2
16
Convert them to Set and use intersect() function:
将它们转换为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
12
You don't need a Set (as the comments above have mentioned).
您不需要一个集合(正如上面的注释所提到的)。
You could instead use a generic function, similar to the one Apple use in their Swift Tour, and thus avoid casting:
相反,你可以使用一个通用的功能,类似于苹果在他们的快速旅行中使用的功能,从而避免选择:
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
}
This function can take any two arrays (SequenceTypes) and if any of their elements are the same it returns true.
这个函数可以取任意两个数组(SequenceTypes),如果它们中的任何一个元素都相同,则返回true。
You could simply modify this generic function to package up an array of strings and return that instead.
您可以简单地修改这个通用函数来打包字符串数组并返回它。
For example like this:
例如像这样:
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
}
Usage like this:
用法如下:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
The added benefit here is that this function also works with all same typed arrays. So later if you need to compare two [myCustomObject]
arrays, once they both conform to equatable, you're all set! (pun intended)
这里的附加好处是,这个函数也适用于所有类型化数组。因此,如果以后需要比较两个[myCustomObject]数组,一旦它们都符合equatable,就都设置好了!(一语双关)
Edit: (For non common elements) you could do something like this
编辑:(对于非公共元素)你可以这样做
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
}
This implementation is ugly though.
然而,这个实现是丑陋的。
#4
2
Swift 3.0
斯威夫特3.0
Use filter to get common elements from two arrays.
使用过滤器从两个数组中获取公共元素。
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)}*/
Output:
输出:
["mango", "blueberry"]
(“芒果”,“蓝莓”)
#5
2
The following works with swift 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)
#6
1
A generic method, inspired by The Swift Programming Language (Swift 3) exercise:
受Swift编程语言(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
}
#7
-2
You can even compare the objects inside the array and filter out common array
您甚至可以比较数组中的对象并过滤掉公共数组
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)