Tìm một đối tượng trong mảng?


144

Swift có cái gì đó như _.findWhere không trong Underscore.js không?

Tôi có một mảng các cấu trúc kiểu Tvà muốn kiểm tra xem mảng có chứa đối tượng struct có thuộc nametính bằng không Foo.

Đã thử sử dụng find()filter()chúng chỉ hoạt động với các kiểu nguyên thủy, ví dụ Stringhoặc Int. Ném một lỗi về việc không tuân thủ Equitablegiao thức hoặc một cái gì đó tương tự.


Đây có thể là những gì bạn đang tìm kiếm: Tìm đối tượng với thuộc tính trong mảng .
Martin R

tại sao không chuyển đổi sang nsdipedia và tìm kiếm
longbow

3
Tôi tin rằng find không còn khả dụng trong Swift 2.0 .. Tôi đã chuyển đổi một số mã 1.2 sang Swift 2.0 và nó nói sẽ sử dụng IndexOf thay thế.
Swift Soda

Câu trả lời:


91

FWIW, nếu bạn không muốn sử dụng chức năng tùy chỉnh hoặc tiện ích mở rộng, bạn có thể:

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

Điều này tạo ra namemảng đầu tiên, sau đófind từ nó.

Nếu bạn có mảng lớn, bạn có thể muốn làm:

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

hoặc có thể:

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

Điều này thậm chí còn tốt hơn. Tôi sẽ đánh dấu đây là câu trả lời vì nhìn tổng thể đơn giản hơn và nó không yêu cầu tạo chức năng tùy chỉnh.
Sahat Yalkabov

27
Kể từ Swift 2.0, bạn có thể sử dụng: Array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
Kể từ Swift 3.0, bạn có thể sử dụng: Array.first (trong đó: {$ 0.name == "Foo"}) nếu bạn cần đối tượng
Brett

Làm cách nào tôi có thể kiểm tra $ 0.name chứa chuỗi "foo"? câu trả lời là về kết hợp chính xác. Tôi cần chứa chuỗi. Bất cứ ai cũng có thể đưa ra cú pháp cho điều đó
Azik Abdullah

289

Chuyển 5

Kiểm tra xem phần tử có tồn tại không

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

Lấy phần tử

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

Lấy phần tử và phần bù của nó

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
}

Lấy phần bù

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

6
Câu trả lời kiểu Swift đẹp!
Zoltán

4
Cảm ơn bạn là người tiết kiệm của tôi, điều này đã làm sạch mã của tôi rất nhiều
Tiến trình quảng cáo

Làm thế nào để kiểm tra nhiều điều kiện nói rằng tôi cần kiểm tra xem mảng có $0.name == "foo"thực hiện một thao tác $0.name == "boo" nào không và thực hiện một thao tác khác
Midhun Narayan

Điều này giúp tôi nhiều hơn câu trả lời được chấp nhận vào năm 2020.
Psiloc

Làm cách nào tôi có thể kiểm tra $ 0.name chứa chuỗi "foo"? Bất cứ ai cũng có thể đưa ra cú pháp cho điều đó
Azik Abdullah

131

Bạn có thể sử dụng indexphương thức khả dụng Arrayvới một vị ngữ ( xem tài liệu của Apple tại đây ).

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

Ví dụ cụ thể của bạn, đây sẽ là:

Swift 5.0

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

Swift 3.0

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

Swift 2.0

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

3
Có, điều này chỉ hoạt động với swift 2.0. Xin lỗi nên đã đề cập đến nó.
dùng3799504

@ user3799504 có nghĩa là $ 0?
Pramod Tapaniya 17/03/2016

$ 0 là viết tắt cho đối số đầu tiên để kết thúc. Trong trường hợp này, nó đề cập đến self.generator.element hoặc từng phần tử của mảng. Vui lòng xem: developer.apple.com/l
Library / ios / document / Swift / Conceptionual / Giả

1
Swift 3 thì sao?
adnako

38

Swift 3

Nếu bạn cần đối tượng sử dụng:

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

(Nếu bạn có nhiều hơn một đối tượng có tên "Foo" thì firstsẽ trả về đối tượng đầu tiên từ một thứ tự không xác định)


Cảm ơn vì điều này. Đây nên là cách lên đó!
NerdyTherapist

3
Điều này thật tuyệt, cảm ơn bạn! Cũng có thể được viết như thế này:array.first {$0.name == "Foo"}
Roland T.

2
Trong Swift3, nó phải làarray.first(where: {$0.name == "Foo"})
Daniel

Với lưu ý của Daniel, đây là câu trả lời đúng, tốt nhất cho Swift 3. Đừng sử dụng bản đồ, bộ lọc cho mục đích này; chúng lặp đi lặp lại trên toàn bộ bộ sưu tập, có thể rất lãng phí.
Womble

20

Bạn có thể lọc mảng và sau đó chỉ cần chọn phần tử đầu tiên, như được hiển thị trong Tìm đối tượng với thuộc tính trong mảng .

Hoặc bạn xác định tiện ích mở rộng tùy chỉnh

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
    }
}

Ví dụ sử dụng:

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
}

Trong Swift 3, bạn có thể sử dụng first(where:)phương thức hiện có (như được đề cập trong một nhận xét ):

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

Về mặt hiệu quả, làm thế nào để so sánh với array.lazy.filter( predicate ).first? Làm thế nào hiệu quả là .lazy cho mảng nhỏ?
Pat Niemeyer

@PatNiemeyer: Tôi không biết, bạn sẽ phải đo hiệu suất và so sánh.
Martin R

@PatNiemeyer giải pháp trên chắc chắn sẽ hiệu quả hơn mặc dù sự khác biệt có lẽ sẽ không lớn. 1. Độ phức tạp của filterluôn luôn O(n)trong khi findFirstMatchingđó chỉ trong trường hợp xấu nhất (khi yếu tố bạn đang tìm kiếm là cuối cùng hoặc không có trong mảng). 2. filtertạo ra một mảng hoàn toàn mới của các phần tử được lọc trong khi phần tử findFirstMatchingchỉ trả về phần tử được yêu cầu.
0101

Trong Swift 3, tôi nhận được lỗi Kế thừa từ loại không giao thức, loại không phải là 'Bool'Sử dụng loại không được khai báo 'T' cho phương thức mở rộng này.
Isuru

@Isuru: Câu trả lời đó khá cũ và được nhắc đến một phiên bản Swift cũ. Trong Swift 3 bạn không cần một phương thức mở rộng tùy chỉnh nữa cho mục đích đó, tôi đã cập nhật câu trả lời tương ứng.
Martin R

20

Swift 3.0

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

Swift 2.1

Lọc trong thuộc tính đối tượng hiện được hỗ trợ trong swift 2.1. Bạn có thể lọc mảng của mình dựa trên bất kỳ giá trị nào của struct hoặc class ở đây là một ví dụ

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

HOẶC LÀ

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

9

Swift 4 ,

Một cách khác để đạt được điều này bằng cách sử dụng chức năng lọc,

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

8

Swift 3

bạn có thể sử dụng chỉ mục (trong đó :) trong Swift 3

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

thí dụ

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

Có một phương pháp trong swift 3 nơi bạn có thể tìm thấy các chỉ mục của một danh sách con của các mục mảng đáp ứng điều kiện $0.name == "Foo"không?
Ahmed Khedr

3

Swift 3

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

2

Swift 2 trở lên

Bạn có thể kết hợp indexOfmapviết hàm "tìm phần tử" trong một dòng.

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")"

Sử dụng filter+ firsttrông sạch hơn, nhưng filterđánh giá tất cả các yếu tố trong mảng. indexOf+ có mapvẻ phức tạp, nhưng việc đánh giá dừng lại khi tìm thấy kết quả khớp đầu tiên trong mảng. Cả hai cách tiếp cận đều có ưu và nhược điểm.


1

Sử dụng contains:

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

Hoặc bạn có thể thử những gì Martin chỉ cho bạn, trong các bình luận và filterthử lại: Tìm đối tượng với thuộc tính trong mảng .


Điều đó sẽ chỉ trả lại một boolean? Tôi cũng cần phải lấy đối tượng, không chỉ kiểm tra xem có trong mảng không.
Sahat Yalkabov

Giả định itemnày là cùng loại với mục trong mảng. Tuy nhiên, tất cả những gì tôi có chỉ là một tiêu đề từ view.annotation.title. Tôi cần so sánh các mục trong mảng theo tiêu đề này.
Sahat Yalkabov

Một cái gì đó như if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov

Martin chỉ cho bạn một cách khác trong các ý kiến. Kiểm tra đường link anh cung cấp.
Christian

1

Một cách khác để có quyền truy cập vào Array.index (of: Any) là khai báo đối tượng của bạn

import Foundation
class Model: NSObject {  }

1

Sử dụng Dollar là Lo-Dash hoặc Underscore.js cho Swift:

import Dollar

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

1

Swift 3:

Bạn có thể sử dụng Swifts tích hợp trong chức năng để tìm các đối tượng tùy chỉnh trong Mảng.

Trước tiên, bạn phải đảm bảo đối tượng tùy chỉnh của mình tuân thủ giao thức : Tương đương .

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)
    }
}

Với chức năng Equitable được thêm vào đối tượng của bạn, Swift sẽ hiển thị cho bạn các thuộc tính bổ sung mà bạn có thể sử dụng trên một mảng:

//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

1

Dành cho Swift 3,

let index = array.index(where: {$0.name == "foo"})

0

Ví dụ: nếu chúng ta có một dãy số:

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

Chúng ta có thể tìm thấy số lẻ đầu tiên như thế này:

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

Điều đó sẽ gửi lại 4 dưới dạng một số nguyên tùy chọn, bởi vì số lẻ đầu tiên (9) nằm ở chỉ số bốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.