如何在Swift中获取2个数组的常用元素列表?

AAA*_*AAA 28 arrays swift

我有两个数组:

fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
Run Code Online (Sandbox Code Playgroud)

如何获得这两个数组中的常用项列表

ouptput = ["mango", "blueberry"]
Run Code Online (Sandbox Code Playgroud)

我无法使用,if contains(array, string)因为我想比较2个数组.

Qby*_*yte 68

您也可以使用filtercontains结合使用:

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)
Run Code Online (Sandbox Code Playgroud)

Setvs 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)
Run Code Online (Sandbox Code Playgroud)

我已经制作了一些(人工)基准测试Int和短/长Strings(10到100 Character秒)(所有随机生成).我总是用array1.count == array2.count

我得到以下结果:

如果你有更多critical #(number of) elements转换为a Set是更可取的

data         |  critical #elements
-------------|--------------------
         Int |        ~50
short String |       ~100
 long String |       ~200
Run Code Online (Sandbox Code Playgroud)

结果说明

使用该Array方法使用"蛮力" - 具有时间复杂性的搜索 O(N^2),N = array1.count = array2.count其与该Set方法形成对比O(N).然而从转换ArraySet和背部是这解释了增加大数据非常昂贵的critical #elements更大的数据类型.


结论

对于Array具有大约100个元素的小s,Array方法很好,但对于较大的方法,你应该使用该Set方法.

如果你想多次使用这个"公共元素" - 操作,建议在可能的情况下使用Sets (元素的类型必须是).Hashable

最后的评论

从转换Array到转换Set是很昂贵的,而从转换转换SetArray非常便宜.

使用filterwith .filter(array1.contains)的性能比.filter{ array1.contains($0) }以下更快:

  • 最后一个创建一个新的闭包(只有一次),而第一个只传递一个函数指针
  • 对于最后一个封闭件的调用创建花费空间和时间的附加堆栈帧(多次:O(N))

  • 只要适用,@Mousavian 基于“Set”的答案会更好,但在类型为“Equatable”而不是“Hashable”的罕见情况下,这很好。 (2认同)

Mou*_*ian 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))
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。顺便说一句,您可以将 `Set` 与任何集合相交(不会损失性能),因此您不需要生成 `vegSet`(本身就是 O(n)`) (2认同)

Woo*_*ock 13

您不需要Set(如上面的评论所述).

您可以改为使用泛型函数,类似于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
}
Run Code Online (Sandbox Code Playgroud)

此函数可以使用任意两个数组(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
}
Run Code Online (Sandbox Code Playgroud)

用法如下:

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


var result = arrayOfCommonElements(one,other)

print(result) //prints [test2, dog, cat]
Run Code Online (Sandbox Code Playgroud)

这里的额外好处是该函数也适用于所有相同类型的数组.所以,以后如果需要比较两个[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
}
Run Code Online (Sandbox Code Playgroud)

但这个实现很丑陋.


Nic*_*uet 5

受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
}
Run Code Online (Sandbox Code Playgroud)

然后,像这样使用它:

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

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

--> commons: [3, 88]
Run Code Online (Sandbox Code Playgroud)