首页 文章

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

提问于
浏览
22

我有两个数组:

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

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

ouptput = ["mango", "blueberry"]

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

7 回答

  • 13

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

    You don't need a Set (as the comments above have mentioned).

    您可以改为使用通用函数,类似于Apple在其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
    }
    

    此函数可以使用任意两个数组(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
    }
    

    Usage like this:

    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

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

    您还可以结合使用 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 方法使用"Brute force" -search,其中time complexity O(N^2) ,其中 N = array1.count = array2.countSet 方法 O(N) 形成对比 . 但是,对于大数据而言,从 ArraySet 的转换非常昂贵,这解释了较大数据类型的 critical #elements 的增加 .


    结论

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

    如果要多次使用此"common elements" -operation,建议尽可能使用 Set s only (元素的类型必须为 Hashable ) .

    最后的评论

    ArraySet 的转换有点贵,而从 SetArray 的转换相比非常便宜 .

    使用 filter.filter(array1.contains).filter{ array1.contains($0) } 更快地提高了性能:

    • 最后一个创建一个新的闭包( only once ),而第一个只传递一个函数指针

    • 对于最后一个,闭包的调用会创建一个额外的堆栈帧,这会花费空间和时间( multiple timesO(N)

  • 19

    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)}*/
    //Different Element
    /*let newArray  = fruitsArray.filter{!vegArray.contains($0)}*/
    

    Output:

    [“芒果”,“蓝莓”]

  • 3

    将它们转换为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))
    

相关问题