根据另一个数组,从数组中移除对象

46

我有两个这样的数组:

var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]

如您所见,JamesSteve是匹配的,我想把他们从arrayA中移除。我应该如何编写代码?

9个回答

73

@francesco-vadicamo在Swift 2/3/4+中的回答

 arrayA = arrayA.filter { !arrayB.contains($0) }

1
应该先将 arrayB 转换为一个 Set - BallpointBen
@BallpointBen 我在答案中添加了一个演示链接:正如您所看到的,它按预期工作。 - Federico Zanetello
@cleexiang,我的回答中有一个演示,你可以使用Swift 4运行它 :) - Federico Zanetello
你试过了吗?我在Xcode 9.2(以及之前的9.0)中刚刚尝试过,它可以工作。如果对你不起作用,请展示一些代码,我会尽力帮助你。 - Federico Zanetello

40

最简单的方法是使用新的Set容器(在Swift 1.2 / Xcode 6.3中添加):

var setA = Set(arrayA)
var setB = Set(arrayB)

// Return a set with all values contained in both A and B
let intersection = setA.intersect(setB) 

// Return a set with all values in A which are not contained in B
let diff = setA.subtract(setB)

如果您想将结果集重新分配给arrayA,只需使用复制构造函数创建一个新实例并将其分配给arrayA:

arrayA = Array(intersection)

不足之处在于您需要创建2个新数据集。 请注意,intersect 不会改变其调用方实例,它只返回一个新的集合。

还有类似的方法可以添加、减去等操作,您可以查看它们。


忘了提到它可用于Swift 1.2 - 我猜你没有使用Xcode 6.3。 - Antonio
啊,好的,你猜对了,我今晚会更新并试一下。谢谢你的帮助。 - Henry Brown
抱歉,我刚意识到这实际上与我想要做的相反,我想要去掉匹配的名称,而不是留下它们?是否可以使用类似的过程来完成这个任务? - Henry Brown
@HenryBrown 是的,我的错误 - 你应该使用 substract,请查看更新后的答案。如果需要了解更多信息,请参阅文档 - 还有其他有用的方法。 - Antonio
我发现这是有史以来最好的答案。看看代码。集合的交集和差集比遍历每个项要高效得多。此外,苹果公司推荐这种更简单的解决方案。只需阅读Swift语言指南:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html - Farini
显示剩余3条评论

20

就像这样:

var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]
for word in arrayB {
    if let ix = find(arrayA, word) {
        arrayA.removeAtIndex(ix)
    }
}
// now arrayA is ["Mike", "Stacey"]

14
这个解决方案对于小型数组非常有效,但应该注意它的复杂度为O(n^2)。对于较大的数组,我会考虑将arrayA转换成一个集合,并将其用于查找 - 这应该将复杂度降至O(2n)。 - Antonio

13

我同意Antonio的回答,但是对于小数组的减法你也可以使用像这样的过滤器闭包:

let res = arrayA.filter { !contains(arrayB, $0) }

1
这很好,因为它保留了顺序。 - Alexander
1
arrayA = arrayA.filter { !arrayB.contains($0) } => 可以工作,上面的代码不行。 - coders

12

matt和freytag的解决方案是唯一考虑重复的答案,并且应该得到比其他答案更多的+1。

以下是Swift 3.0版本的matt答案的更新版本:

var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]
for word in arrayB {
    if let ix = arrayA.index(of: word) {
        arrayA.remove(at: ix)
    }
}

9
这也可以实现为一个减法函数:
func -<T:RangeReplaceableCollectionType where T.Generator.Element:Equatable>( lhs:T, rhs:T ) -> T {

    var lhs = lhs
    for element in rhs {
        if let index = lhs.indexOf(element) { lhs.removeAtIndex(index) }
    }

    return lhs
}

现在你可以使用。
arrayA - arrayB

更新的 Swift 5 实现。
func -<T: RangeReplaceableCollection>(lhs: T, rhs: T) -> T where T.Iterator.Element: Equatable {

    var lhs = lhs
    for element in rhs {
        if let index = lhs.firstIndex(of: element) { lhs.remove(at: index) }
    }

    return lhs
}

5

使用Antonio提到的Array → Set → Array方法,并结合freytag指出的操作符的便利性,我非常满意地使用了以下方法:

// Swift 3.x/4.x
func - <Element: Hashable>(lhs: [Element], rhs: [Element]) -> [Element]
{
    return Array(Set<Element>(lhs).subtracting(Set<Element>(rhs)))
}

4

对于较小的数组,我使用以下方法:

/* poormans sub for Arrays */

extension Array where Element: Equatable {

    static func -=(lhs: inout Array, rhs: Array) {

        rhs.forEach {
            if let indexOfhit = lhs.firstIndex(of: $0) {
                lhs.remove(at: indexOfhit)
            }
        }
    }

    static func -(lhs: Array, rhs: Array) -> Array {

        return lhs.filter { return !rhs.contains($0) }
    }
}

1
使用索引数组删除元素:
  1. Array of Strings and indexes

    let animals = ["cats", "dogs", "chimps", "moose", "squarrel", "cow"]
    let indexAnimals = [0, 3, 4]
    let arrayRemainingAnimals = animals
        .enumerated()
        .filter { !indexAnimals.contains($0.offset) }
        .map { $0.element }
    
    print(arrayRemainingAnimals)
    
    //result - ["dogs", "chimps", "cow"]
    
  2. Array of Integers and indexes

    var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    let indexesToRemove = [3, 5, 8, 12]
    
    numbers = numbers
        .enumerated()
        .filter { !indexesToRemove.contains($0.offset) }
        .map { $0.element }
    
    print(numbers)
    
    //result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
    



使用另一个数组的元素值来删除元素

  1. Arrays of integers

    let arrayResult = numbers.filter { element in
        return !indexesToRemove.contains(element)
    }
    print(arrayResult)
    
    //result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
    
  2. Arrays of strings

    let arrayLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    let arrayRemoveLetters = ["a", "e", "g", "h"]
    let arrayRemainingLetters = arrayLetters.filter {
        !arrayRemoveLetters.contains($0)
    }
    
    print(arrayRemainingLetters)
    
    //result - ["b", "c", "d", "f", "i"]
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接