在数组中查找具有属性的对象

74

有没有可能通过特定属性从数组中获取对象?还是我需要循环遍历数组中的所有对象,并检查属性是否是我要查找的特定属性?

编辑:感谢你指导我正确的方向,但我遇到了将其转换的问题。

// 再次编辑:好的,如果只有一个特定的结果呢?这也是一种可能的方法吗?

let imageUUID = sender.imageUUID


let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!

    // this is working
    //var imageObject:Images!
    /*
    for (index, image) in enumerate(questionImageObjects) {

        if(image.imageUUID == imageUUID) {
            imageObject = image
        }

    }
    */

// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
5个回答

158

// 这不起作用 - NSArray不是Images的子类型 - 那么如果只有1个可能的结果怎么办?

在数组上,你无法在编译时证明只有一个可能的结果。实际上,你要求的是第一个匹配结果。最简单的方法(虽然不是最快的方法)是只取过滤结果的第一个元素:

let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first

imageObject现在将成为一个可选项,因为有可能没有匹配的内容。

如果搜索整个数组太耗时,当然可以轻松创建一个firstMatching函数,它将返回符合条件的第一个(可选)元素,但对于短数组来说,这很简单。


正如Charles所指出的,在Swift 3中,这是内置的:

questionImageObjects.first(where: { $0.imageUUID == imageUUID })

3
你可以在中间添加lazy函数,这样它只会扫描数组直到找到第一个匹配项,并避免你所提到的性能问题:let imageObject = questionImageObjects.lazy.filter{ $0.imageUUID == imageUUID }.first - charles
13
实际上,“questionImageObjects.first(where: { $0.imageUUID == imageUUID })”可能是使用Swift 3的正确方法。 - charles

32

编辑于2016年5月5日:Swift 3将包括first(where:)

在Swift 2中,您可以使用indexOf查找与谓词匹配的第一个数组元素的索引。

let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})

filter相比,它更快,因为它会在第一次匹配后停止。(或者,您可以使用一个lazy序列。)

然而,有点让人烦恼的是,你只能得到索引,而不能得到对象本身。为了方便起见,我使用以下扩展:

extension CollectionType {
    func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
        return try indexOf(predicate).map({self[$0]})
    }
}

接下来的工作如下:

questionImageObjects.find({$0.imageUUID == imageUUID})

14

是的,您可以使用filter方法,该方法接受一个闭包,在其中可以设置您的逻辑表达式。

示例:

struct User {
    var firstName: String?
    var lastName: String?
}

let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];

let johns = users.filter( { return $0.firstName == "John" } )

请注意,filter返回一个包含所有满足逻辑表达式的项的数组。

有关更多信息,请参阅库参考


你也可以使用NSPredicate并使用一个NSArray。 - William Falcon
如何按照名字和姓氏进行过滤? - Kaptain
1
@Kaptain:这应该很简单:将return $0.firstName == "John"替换为return $0.firstName == "John" && $0.lastName == "Doe"。换句话说,只需重写/扩展传递给过滤器的闭包,确定何时应包含或不包含元素。 - Antonio

11

这里是一个使用 Swift 5 的工作示例。

class Point{
    var x:Int
    var y:Int
    
    init(x:Int, y:Int){
        self.x = x
        self.y = y
    }
}

var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]

// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}

// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}

参考资料: 尾随闭包


3

以下是另一种方法,通过使用对象属性在数组中搜索特定对象来获取该对象。

if arrayTicketsListing.contains({ $0.status_id == "2" }) {
      let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
      print(ticketStatusObj.status_name)
}  

我的 arrayTicketsListing 是一个包含 TicketsStatusList 类对象的数组 [TicketsStatusList]

// TicketsStatusList class

class TicketsStatusList {
    internal var status_id: String
    internal var status_name: String
    init(){
        status_id = ""
        status_name = ""
    }
}

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