如何在swift中获得2个数组的常用元素列表
我有两个数组:
fruitsArray = ["apple", "mango", "blueberry", "orange"] vegArray = ["tomato", "potato", "mango", "blueberry"]
我怎样才能得到这两个数组中的常用项目列表
ouptput = ["mango", "blueberry"]
我不能使用, if contains(array, string)
因为我想比较2个数组。 请帮忙
你不需要一个Set(如上面提到的评论)。
你可以使用一个通用函数,类似于苹果在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。
你可以简单地修改这个generics函数来打包一个string数组,然后返回。
比如像这样:
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]
这里增加的好处是这个函数也适用于所有相同types的数组。 所以后来如果你需要比较两个[myCustomObject]
数组,一旦它们都符合equatable,你就全部设置了 ! (双关语意)
编辑:(对于非常见的元素),你可以做这样的事情
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 }
这个实现很丑陋。
您也可以使用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
Set
为单个计算常用元素
我们考虑下面的代码片段:
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
)(都是随机生成的)做了一些(人为的)基准。 我总是使用array1.count == array2.count
我得到以下结果:
如果你有更多的critical #(number of) elements
转换成一个Set
是最好的
data | critical #elements -------------|-------------------- Int | ~50 short String | ~100 long String | ~200
结果的解释
使用Array
方法使用具有时间复杂度 O(N^2)
“暴力”search,其中N = array1.count = array2.count
,这与Set
方法O(N)
相反。 然而,对于大数据来说,从Array
到大数据的转换是非常昂贵的,这就解释了对于更大数据types的critical #elements
的增加。
结论
对于具有大约100个元素的小型Array
, Array
方法很好,但对于较大的Array
,您应该使用Set
方法。
如果要多次使用这个“通用元素”操作,build议只在可能的情况下使用Set
(元素的types必须是Hashable
)。
最后的评论
从Array
到Array
的转换是昂贵的,而从Array
到Array
的转换相反却非常便宜。
使用.filter(array1.contains)
filter
性能比.filter{ array1.contains($0) }
快,因为:
- 最后一个创build一个新的闭包( 只有一次 ),而第一个只传递一个函数指针
- 对于最后一个闭包的调用会创build一个额外的栈空间和时间( 多次 :
O(N)
)
将它们转换为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))
Swift 3.0
使用filter从两个数组中获取公共元素。
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)}*/
输出:
[“芒果”,“蓝莓”]
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 }
你甚至可以比较数组内的对象,并过滤出常用数组
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)