如何在Swift中获取2数组的公共元素列表?

vohkndzv  于 2022-11-21  发布在  Swift
关注(0)|答案(7)|浏览(192)

我有两个数组:

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

如何获得这两个数组中的公共项列表

ouptput = ["mango", "blueberry"]

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

kgsdhlau

kgsdhlau1#

您也可以将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)

SetArray对公共元素的单次计算

我们考虑以下代码片段:

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(10到100个Character)(全部随机生成)做了一些(人工)基准测试。
我得到以下结果:
如果你有超过critical #(number of) elements转换为一个Set是可取的

data         |  critical #elements
-------------|--------------------
         Int |        ~50
short String |       ~100
 long String |       ~200

结果说明

使用Array方法使用“暴力”搜索,其具有time complexityO(N^2),其中N = array1.count = array2.count,这与Set方法O(N)形成对比。然而,对于大型数据,从ArraySet的转换以及从SetO(N^2)的转换非常昂贵,这解释了对于更大的数据类型,critical #elements的增加。

结论

对于具有大约100个元素的小ArrayArray方法是合适的,但是对于较大的元素,您应该使用Set方法。
如果要多次使用此“公共元素”操作,建议在可能的情况下仅使用Set s****(元素类型必须为Hashable)。

最后备注

ArraySet的转换是一种昂贵的转换,而从SetArray的转换相比之下是非常便宜的。
filter.filter(array1.contains)配合使用,在性能方面比.filter{ array1.contains($0) }快,原因如下:

  • 最后一个创建一个新的闭包(仅一次),而第一个只传递一个函数指针
  • 对于最后一个,闭包的调用创建了额外的栈帧,这花费了空间和时间(多次:一个月30个月1x)
dpiehjr4

dpiehjr42#

将它们转换为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.intersection(vegSet))
5cg8jx4n

5cg8jx4n3#

您不需要Set(正如上面的注解所提到的)。

你可以使用一个 generic 函数,类似于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
}

不过,这种实现很难看。

ruoxqz4g

ruoxqz4g4#

一种通用方法,灵感来自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]
vdzxcuhz

vdzxcuhz5#

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

vmpqdwk36#

以下内容适用于swift 4,5

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
     
//You can using filter function
let commonArray = fruitsArray.filter { fruit in
    vegArray.contains(fruit)
}

print(commonArray)

输出:[“芒果”,“蓝莓”]

eivgtgni

eivgtgni7#

使用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]))

相关问题