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

时间:2015-09-07 12:54:44

标签: ios arrays swift

我有两个数组:

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

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

ouptput = ["mango", "blueberry"]

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

6 个答案:

答案 0 :(得分:58)

您还可以结合使用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)

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和短/长String s(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)方法使用“强力”搜索,其中time complexity N = array1.count = array2.count其中SetO(N)方法{{1}形成鲜明对比1}}。但是,对于大型数据,从ArraySet的转换非常昂贵,这解释了较大数据类型critical #elements的增加。

结论

对于包含大约100个元素的小ArrayArray方法很好,但对于较大的方法,您应该使用Set方法。

如果您想多次使用此“常用元素” - 操作,建议尽可能使用Set s (元素的类型必须为{{1} }})。

最后备注

HashableArray的转换价格昂贵,而从SetSet的转换相比非常便宜。

Arrayfilter一起使用,性能明显快于.filter(array1.contains),因为:

  • 最后一个创建一个新的闭包(只有一次),而第一个只传递一个函数指针
  • 对于最后一个,闭包的调用会创建一个额外的堆栈框架,这会占用空间和时间(多次.filter{ array1.contains($0) }

答案 1 :(得分: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))

答案 2 :(得分:12)

您不需要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
}

此函数可以使用任意两个数组(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]数组,一旦它们都符合equatable,你就是 set ! (双关语)

编辑:(对于常用元素)你可以做类似的事情

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
}

但这个实现很丑陋。

答案 3 :(得分:3)

一种通用方法,受The 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]

答案 4 :(得分:3)

以下适用于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)

答案 5 :(得分: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]))