如何检查一个元素是否在数组中

567
在Swift中,我如何检查一个元素是否存在于数组中?Xcode没有任何关于containincludehas的建议,在书中快速搜索也没有结果。有什么想法可以检查这个吗?我知道有一个方法find可以返回索引号,但是否有像Ruby的#include?那样返回布尔值的方法?

我需要的示例:

var elements = [1,2,3,4,5]
if elements.contains(5) {
  //do something
}

11
if find(elements, 5) != nil { } 不够好吗? (备注:该行代码的意思为如果在数组"elements"中找到值为5的元素,则执行大括号内的代码。) - Martin R
1
我本来希望有更简洁的解决方案,但现在看起来不太乐观。我还没有在文档或书籍中找到任何相关内容。 - jaredsmith
18个回答

993

Swift 2、3、4、5:

let elements = [1, 2, 3, 4, 5]
if elements.contains(5) {
    print("yes")
}

contains()协议扩展方法,属于SequenceType(用于具有Equatable元素的序列),而不是像早期版本中的全局方法。

备注:

Swift旧版本:

let elements = [1,2,3,4,5]
if contains(elements, 5) {
    println("yes")
}

4
有没有关于这种全局函数的文档? - Rivera
3
如果数组中的每个项(以及我们要搜索的项)都是 Dictionary<String, AnyObject> 类型,这段代码是否能正常工作?我试图实现这个功能,但出现了编译时错误。 - ppalancica
8
这需要数组元素符合 Equatable 协议(Dictionary<String, AnyObject> 不符合)。contains() 还有一种带谓词的变体(参见 https://dev59.com/e10b5IYBdhLWcg3wCtXY),也许你可以使用它,例如:`if contains(array, { $0 == dict } ) ...` - Martin R
如何从通用数组中搜索特定元素?比如 [AnyObject]? - Dhaval H. Nena

155

如果您在这里寻找如何从数组中查找并删除对象的方法:

Swift 1

if let index = find(itemList, item) {
    itemList.removeAtIndex(index)
}

Swift 2

if let index = itemList.indexOf(item) {
    itemList.removeAtIndex(index)
}

Swift 3,4

if let index = itemList.index(of: item) {
    itemList.remove(at: index)
}

Swift 5.2

if let index = itemList.firstIndex(of: item) {
    itemList.remove(at: index)
}

70

更新为Swift 2+

请注意,从Swift 3(甚至2)开始,下面的扩展不再必要,因为全局contains函数已经成为Array上一对扩展方法,允许您执行以下任一操作:

let a = [ 1, 2, 3, 4 ]

a.contains(2)           // => true, only usable if Element : Equatable

a.contains { $0 < 1 }   // => false

Swift 1 的历史解决方案:

使用以下扩展:(更新至 Swift 5.2

 extension Array {
     func contains<T>(obj: T) -> Bool where T: Equatable {
         return !self.filter({$0 as? T == obj}).isEmpty
     }
 }

用法:

array.contains(1)

1
根据您习惯的不同,.contains可能会感觉更直观和易记。 - Pirijan
4
你能否分解一下你的句法?我从未见过这样的格式,并且你同时使用了很多高级的语言结构!请帮我理解一下。 - Aggressor
我已更改原始代码,使用.isEmpty,因为这是SwiftLint建议的做法。 - Douglas Frari
不要使用这个扩展,它很糟糕。只需使用https://developer.apple.com/documentation/swift/array/2945493-contains - NoKey
1
当那些对语言历史没有概念的人做出这样的判断时,真是太棒了。请注意“已更新为Swift 2/3”部分,那时contains被添加到了语言中。 - David Berry

46
如果您要检查自定义类或结构体的实例是否包含在数组中,则需要实现Equatable协议,然后才能使用.contains(myObject)。
例如:
struct Cup: Equatable {
    let filled:Bool
}

static func ==(lhs:Cup, rhs:Cup) -> Bool { // Implement Equatable
    return lhs.filled == rhs.filled
}

那么你可以执行:

cupArray.contains(myCup)

提示:重载运算符应该放在全局层次,而不是在你的类/结构体内部。


注意:这并不检查自定义类或结构体的实例是否在数组中。它检查是否有一个元素,其==运算符在与contains()参数进行比较时评估为true。例如:一个指针包装对象数组,实现==以返回两个对象是否指向相同的内存地址,将完全破坏此解决方案。 - Kevin

32

我使用了过滤器。

let results = elements.filter { el in el == 5 }
if results.count > 0 {
    // any matching items are in results
} else {
    // not found
}

如果您愿意,可以将其压缩为

if elements.filter({ el in el == 5 }).count > 0 {
}

希望这有所帮助。


Swift 2更新

默认实现万岁!

if elements.contains(5) {
    // any matching items are in results
} else {
    // not found
}

我喜欢过滤器解决方案,因为你可以用它来处理各种事情。例如,我正在移植一些代码,这些代码循环并尝试查看列表是否已经有一个包含字符串值的字段项。在Swift中,使用该字段的过滤器只需一行代码即可完成。 - Maury Markowitz
1
过滤器效率低下,因为它总是循环遍历所有元素,而不是在找到元素时立即返回。最好使用find()代替。 - Thorsten

26

(Swift 3)

检查数组中是否存在符合某些条件的元素,如果存在,则继续使用第一个这样的元素

如果意图是:

  1. 检查数组中是否存在符合某些布尔条件(不一定是相等测试)的元素,
  2. 如果存在,则继续并使用第一个这样的元素,

则作为蓝本化的 Sequence,与 contains(_:) 的另一种选择是使用 Sequencefirst(where:)

let elements = [1, 2, 3, 4, 5]

if let firstSuchElement = elements.first(where: { $0 == 4 }) {
    print(firstSuchElement) // 4
    // ...
}
在这个虚构的例子中,它的使用可能看起来很愚蠢,但是如果查询非基本元素类型的数组是否存在满足某些条件的任何元素,它非常有用。例如。
struct Person {
    let age: Int
    let name: String
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name
    }
}

let persons = [Person(17, "Fred"),   Person(16, "Susan"),
               Person(19, "Hannah"), Person(18, "Sarah"),
               Person(23, "Sam"),    Person(18, "Jane")]

if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
    print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
    // ...
} // Hannah can possibly drive the rental car in Sweden.

let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
    print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
    // ...
} // Sarah is the same age as Daniel.
任何使用 .filter { ... some condition }.first 的链式操作都可以优化为使用 first(where:)。后者更能表达意图,并且相比于可能非惰性的应用 .filter,具有性能优势,因为这些操作将在提取(可能)通过筛选的第一个元素之前传递整个数组。

检查数组中是否存在满足某些条件的元素,并删除第一个符合条件的元素

下面的评论询问:

如何从数组中删除 firstSuchElement

与上述用例类似,要删除符合给定谓词的第一个元素。为此,可以使用Collectionindex(where:)方法(其对于数组集合是可用的)查找满足谓词的第一个元素的索引,然后可以使用该索引与 Arrayremove(at:) 方法来(可能;如果存在)删除该元素。

var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]

if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
    elements.remove(at: indexOfFirstSuchElement)
    print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}

或者,如果你想要从数组中删除元素 并处理,可以应用 Optional:s map(_:) 方法条件地(对于从 index(where:) 返回的 .some(...))使用来自 index(where:) 的结果来删除和捕获数组中已移除的元素(在可选绑定子句中)。

var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]

if let firstSuchElement = elements.index(where: { $0 == "c" })
    .map({ elements.remove(at: $0) }) {

    // if we enter here, the first such element have now been
    // remove from the array
    print(elements) // ["a", "b", "d", "e", "a", "b", "c"]

    // and we may work with it
    print(firstSuchElement) // c
}
请注意,在上面的人为示例中,数组成员是简单值类型(String实例),因此使用谓词查找给定成员有点过度,因为我们可以使用更简单的 index(of :) 方法进行相等性测试,如@DogCoffee's answer所示。但是,如果将上述查找和删除方法应用于Person示例,则使用带有谓词的index(where:)是适当的(因为我们不再测试相等性而是测试是否满足提供的谓词)。

我该如何从数组中删除第一个元素? - i6x86
@i6x86 感谢您的提问。我更新了我的答案,并提供了一个示例,说明如何删除元素(以及如何删除并捕获已删除的元素)。 - dfrib

21
一个包含属性等于某值的数组。
yourArray.contains(where: {$0.propertyToCheck == value })

返回布尔值。


14

完成这个最简单的方法是在数组上使用筛选器。

let result = elements.filter { $0==5 }

如果元素存在,result将包含找到的元素,否则它将为空。因此,仅检查result是否为空将告诉您该元素是否存在于数组中。我会使用以下内容:

if result.isEmpty {
    // element does not exist in array
} else {
    // element exists
}

很好的解决方案。所以这个方法返回一个数组。然而,我正在使用它来查找一个“id”。在我的应用程序中,“d”是唯一的,因此只能有一个结果。有没有办法只返回一个结果?目前我正在使用result [0]。 - Dan Beaulieu
3
像这样做:let result = elements.filter { $0==5 }.first,应该可以达到你想要的效果。 - davetw12
@davetw12 不需要遍历整个集合。最好使用 first(where: predicate) - Leo Dabus

10

Swift 4/5

使用 filter 函数也可以实现这一点。

var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
    print("found")
} else {
    print("not found")
}

这会不必要地迭代整个集合。这绝对是错误的方法。 - Leo Dabus

6

从Swift 2.1开始,NSArray有containsObject方法可以这样使用:

if myArray.containsObject(objectImCheckingFor){
    //myArray has the objectImCheckingFor
}

4
实际上,那是针对NSArray的,不是Swift数组。 - Tycho Pandelaar
是的,但是你可以将你的 Swift 数组临时转换为 NSArray:if let tempNSArrayForChecking = mySwiftArray as NSArray? where tempNSArrayForChecking.containsObject(objectImCheckingFor) {//myArray has the object } - Vitalii

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