在数组中找到对象?


144

Swift是否有类似_.findWhere的内容中的内容?

我有一个类型为struct的数组,T想检查array是否包含name属性等于的struct对象Foo

尝试使用 find()filter()但它们仅适用于基本类型,例如StringInt。引发有关不符合Equitable协议之类的错误。


这可能是您要查找的内容:在Array中查找具有属性的对象
Martin R

为什么不转换为nsdictionary并搜索
longbow 2015年

3
我相信find在Swift 2.0中不再可用。我将一些1.2代码转换为Swift 2.0,并说它使用IndexOf。
Swift Soda

Answers:


91

FWIW,如果您不想使用自定义功能或扩展名,则可以:

let array = [ .... ]
if let found = find(array.map({ $0.name }), "Foo") {
    let obj = array[found]
}

name首先生成数组,然后find从中。

如果阵列很大,则可能需要执行以下操作:

if let found = find(lazy(array).map({ $0.name }), "Foo") {
    let obj = array[found]
}

或许:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}

这样更好。我将其标记为答案,因为它总体上看起来更简单,并且不需要创建自定义函数。
Sahat Yalkabov 2015年

27
从Swift 2.0开始,您可以使用:array.indexOf({$ 0.name ==“ Foo”})
tf.alves 2015年

73
从Swift 3.0开始,如果需要该对象,则可以使用:array.first(where:{$ 0.name ==“ Foo”})
Brett

如何检查$ 0.name是否包含字符串“ foo”?答案是关于精确匹配。我需要包含字符串。任何人都可以提供其语法
Azik Abdullah

289

SWIFT 5

检查元素是否存在

if array.contains(where: {$0.name == "foo"}) {
   // it exists, do something
} else {
   //item could not be found
}

获取元素

if let foo = array.first(where: {$0.name == "foo"}) {
   // do something with foo
} else {
   // item could not be found
}

获取元素及其偏移量

if let foo = array.enumerated().first(where: {$0.element.name == "foo"}) {
   // do something with foo.offset and foo.element
} else {
   // item could not be found
}

获取偏移

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}

6
不错的Swift风格的答案!
佐尔坦

4
谢谢你是我的救星,这已经清理了我的代码很多
AD Progress

如何检查多个条件说我需要检查阵列是否 $0.name == "foo"执行一项操作并 $0.name == "boo" 执行另一项操作
Midhun Narayan19年

这对我的帮助超过了2020
。– Psiloc

如何检查$ 0.name是否包含字符串“ foo”?任何人都可以提供其语法
Azik Abdullah

131

您可以使用谓词index上可用的方法Array请参阅此处的Apple文档)。

func index(where predicate: (Element) throws -> Bool) rethrows -> Int?

对于您的特定示例,这将是:

斯威夫特5.0

if let i = array.firstIndex(where: { $0.name == "Foo" }) {
    return array[i]
}

斯威夫特3.0

if let i = array.index(where: { $0.name == Foo }) {
    return array[i]
}

雨燕2.0

if let i = array.indexOf({ $0.name == Foo }) {
    return array[i]
}

3
是的,这仅适用于swift 2.0。对不起,应该提到它。
user3799504 2015年

@ user3799504的含义是$ 0?
Pramod Tapaniya '16

$ 0是闭包的第一个参数的简写形式。在这种情况下,它引用self.generator.element或数组的每个元素。请参阅:developer.apple.com/library/ios/documentation/Swift/Conceptual/…–
user3799504

1
Swift 3呢?
adnako '16

38

迅捷3

如果需要该对象,请使用:

array.first{$0.name == "Foo"}

(如果有多个名为“ Foo” first的对象,则将以未指定的顺序返回第一个对象)


这次真是万分感谢。这应该在那里!
NerdyTherapist

3
很好,谢谢!也可以这样写:array.first {$0.name == "Foo"}
Roland T.

2
Swift3中必须为array.first(where: {$0.name == "Foo"})
Daniel

有了Daniel的注释,这是Swift 3的正确,最佳答案。不要为此目的使用map,filter;他们遍历整个集合,这可能是非常浪费的。
Womble

20

您可以过滤数组,然后选择第一个元素,如“ 在数组查找带有属性的对象”中所示

或者您定义自定义扩展

extension Array {

    // Returns the first element satisfying the predicate, or `nil`
    // if there is no matching element.
    func findFirstMatching<L : BooleanType>(predicate: T -> L) -> T? {
        for item in self {
            if predicate(item) {
                return item // found
            }
        }
        return nil // not found
    }
}

用法示例:

struct T {
    var name : String
}

let array = [T(name: "bar"), T(name: "baz"), T(name: "foo")]

if let item = array.findFirstMatching( { $0.name == "foo" } ) {
    // item is the first matching array element
} else {
    // not found
}

Swift 3中,您可以使用现有first(where:)方法(如注释中所述):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}

就效率而言,这与之相比array.lazy.filter( predicate ).first呢?.lazy对于小型阵列的效率如何?
Pat Niemeyer

@PatNiemeyer:我不知道,您必须衡量性能并进行比较。
Martin R

@PatNiemeyer上面的解决方案无疑会更高效,即使差异可能不会很大。1.的filter总是复杂的,O(n)而在情况中findFirstMatching,只有在最坏的情况下(当您要查找的元素是数组中的最后一个或根本不在数组中时),它总是很复杂的。2. filter创建一个全新的过滤元素数组,而findFirstMatching刚刚返回所请求的元素。
0101年

在Swift 3中,我从该协议的扩展方法的非协议,非类类型'Bool'继承使用未声明的类型'T'得到了错误继承
Isuru

@Isuru:这个答案很老,是旧的Swift版本。在Swift 3中,您不再需要为此目的使用自定义扩展方法,我已经相应地更新了答案。
Martin R

20

斯威夫特3.0

if let index = array.index(where: { $0.name == "Foo" }) {
    return array[index]
}

斯威夫特2.1

swift 2.1现在支持对象属性过滤。您可以根据struct或class的任何值过滤数组,这是一个示例

for myObj in myObjList where myObj.name == "foo" {
 //object with name is foo
}

要么

for myObj in myObjList where myObj.Id > 10 {
 //objects with Id is greater than 10
}

9

斯威夫特4

使用过滤器功能实现此目标的另一种方法,

if let object = elements.filter({ $0.title == "title" }).first {
    print("found")
} else {
    print("not found")
}

8

迅捷3

您可以在Swift 3中使用index(where :)

func index(where predicate: @noescape Element throws -> Bool) rethrows -> Int?

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}

swift 3中是否有一种方法可以在其中找到满足条件的数组项的子列表的索引$0.name == "Foo"
艾哈迈德·赫德

3

迅捷3

if yourArray.contains(item) {
   //item found, do what you want
}
else{
   //item not found 
   yourArray.append(item)
}

2

Swift 2或更高版本

您可以合并indexOfmap在一行中编写“查找元素”功能。

let array = [T(name: "foo"), T(name: "Foo"), T(name: "FOO")]
let foundValue = array.indexOf { $0.name == "Foo" }.map { array[$0] }
print(foundValue) // Prints "T(name: "Foo")"

使用filter+ first看起来更干净,但是会filter评估数组中的所有元素。indexOf+ map看起来很复杂,但是当找到数组中的第一个匹配项时,评估就会停止。两种方法都各有利弊。


1

用途contains

var yourItem:YourType!
if contains(yourArray, item){
    yourItem = item
}

或者,您可以尝试在注释中尝试Martin指出的内容,然后filter再尝试一次:在Array中查找具有Property的对象


那只会返回布尔值吗?我也需要获取对象,而不仅仅是检查数组中是否存在。
Sahat Yalkabov 2015年

该假设item与数组中的项具有相同的类型。但是,我所拥有的只是的标题view.annotation.title。我需要按此标题比较数组中的项目。
Sahat Yalkabov 2015年

有点像if contains(yourArray, view.annotation.title) { // code goes here }
Sahat Yalkabov 2015年

马丁在评论中向您展示了另一种方式。检查他提供的链接。
基督教徒

1

获取array.index(of:Any)的另一种方法是声明您的对象

import Foundation
class Model: NSObject {  }

1

对Swift 使用Lo-Dash或Underscore.js 美元

import Dollar

let found = $.find(array) { $0.name == "Foo" }

1

斯威夫特3:

您可以使用Swifts内置功能在数组中查找自定义对象。

首先,您必须确保您的自定义对象符合:Equatable protocol

class Person : Equatable { //<--- Add Equatable protocol
    let name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    //Add Equatable functionality:
    static func == (lhs: Person, rhs: Person) -> Bool {
        return (lhs.name == rhs.name)
    }
}

将Equatable功能添加到对象后,Swift现在将向您显示可在数组上使用的其他属性:

//create new array and populate with objects:
let p1 = Person(name: "Paul", age: 20)
let p2 = Person(name: "Mike", age: 22)
let p3 = Person(name: "Jane", age: 33)
var people = [Person]([p1,p2,p3])

//find index by object:
let index = people.index(of: p2)! //finds Index of Mike

//remove item by index:
people.remove(at: index) //removes Mike from array


0

例如,如果我们有一个数字数组:

let numbers = [2, 4, 6, 8, 9, 10]

我们可以找到这样的第一个奇数:

let firstOdd = numbers.index { $0 % 2 == 1 }

这将作为可选整数发送回4,因为第一个奇数(9)在索引4处。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.