Temukan objek dalam array?

144

Apakah Swift memiliki sesuatu seperti _.findWhere di Underscore.js?

Saya memiliki array tipe struct Tdan ingin memeriksa apakah array berisi objek struct yang namepropertinya sama dengan Foo.

Sudah mencoba menggunakan find()dan filter()tetapi mereka hanya bekerja dengan tipe primitif, misalnya Stringatau Int. Melempar kesalahan tentang tidak mematuhi Equitableprotokol atau sesuatu seperti itu.

Sahat Yalkabov
sumber
Ini bisa menjadi apa yang Anda cari: Temukan Objek dengan Properti di Array .
Martin R
mengapa tidak mengonversi ke nsdictionary dan mencari
longbow
3
Saya percaya menemukan tidak lagi tersedia di Swift 2.0 .. Saya mengubah beberapa kode 1.2 ke Swift 2.0 dan dikatakan menggunakan IndexOf sebagai gantinya.
Swift Soda

Jawaban:

91

FWIW, jika Anda tidak ingin menggunakan fungsi atau ekstensi khusus, Anda dapat:

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

Ini menghasilkan namearray pertama, lalu finddarinya.

Jika Anda memiliki array besar, Anda mungkin ingin melakukan:

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

atau mungkin:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}
rintaro
sumber
Ini bahkan lebih baik. Saya akan menandai ini sebagai jawaban karena secara keseluruhan terlihat lebih sederhana dan tidak perlu membuat fungsi kustom.
Sahat Yalkabov
27
Mulai dari Swift 2.0, Anda dapat menggunakan: array.indexOf ({$ 0.name == "Foo"})
tf.alves
73
Pada Swift 3.0 Anda dapat menggunakan: array.first (di mana: {$ 0.name == "Foo"}) jika Anda memerlukan objek
Brett
Bagaimana saya dapat memeriksa $ 0.name berisi string "foo"? jawaban tentang pencocokan tepat. Saya perlu berisi string. Adakah yang bisa memberikan sintaks untuk itu
Azik Abdullah
290

SWIFT 5

Periksa apakah ada elemen

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

Dapatkan elemennya

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

Dapatkan elemen dan offsetnya

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
}

Dapatkan offsetnya

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}
Daniel
sumber
6
Jawaban gaya Swift yang bagus!
Zoltán
4
Terima kasih Anda adalah penghemat saya, ini telah banyak membersihkan kode saya
Kemajuan AD
cara memeriksa beberapa kondisi mengatakan saya perlu memeriksa apakah array $0.name == "foo"melakukan satu operasi dan $0.name == "boo" melakukan operasi lain
Midhun Narayan
Ini membantu saya lebih dari jawaban yang diterima pada tahun 2020.
Psiloc
Bagaimana saya dapat memeriksa $ 0.name berisi string "foo"? Adakah yang bisa memberikan sintaks untuk itu
Azik Abdullah
131

Anda dapat menggunakan indexmetode yang tersedia Arraydengan predikat ( lihat dokumentasi Apple di sini ).

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

Untuk contoh spesifik Anda, ini akan menjadi:

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]
}
pengguna3799504
sumber
3
Ya ini bekerja hanya dengan swift 2.0. Maaf seharusnya menyebutkannya.
user3799504
@ user3799504 artinya $ 0?
Pramod Tapaniya
$ 0 adalah singkatan untuk argumen pertama hingga penutupan. Dalam hal ini mengacu pada elemen self.generator.element atau setiap elemen dari array. Silakan lihat: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504
1
Bagaimana dengan Swift 3?
adnako
38

Cepat 3

Jika Anda perlu menggunakan objek:

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

(Jika Anda memiliki lebih dari satu objek bernama "Foo" maka firstakan mengembalikan objek pertama dari pemesanan yang tidak ditentukan)

Brett
sumber
Terima kasih untuk ini. Ini pasti jauh di atas sana!
NerdyTherapist
3
Ini bagus, terima kasih! Dapat ditulis seperti ini juga:array.first {$0.name == "Foo"}
Roland T.
2
Di Swift3 itu pastiarray.first(where: {$0.name == "Foo"})
Daniel
Dengan catatan Daniel, ini THE benar, jawaban terbaik untuk Swift 3. Jangan gunakan peta, filter untuk tujuan ini; mereka beralih ke seluruh koleksi, yang bisa sangat boros.
Womble
20

Anda bisa memfilter array dan kemudian hanya memilih elemen pertama, seperti yang ditunjukkan pada Find Object with Property in Array .

Atau Anda menentukan ekstensi khusus

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

Contoh penggunaan:

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
}

Di Swift 3 Anda dapat menggunakan first(where:)metode yang ada (seperti yang disebutkan dalam komentar ):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}
Martin R
sumber
Dalam hal efisiensi, bagaimana perbandingannya dengan ini array.lazy.filter( predicate ).first? Seberapa efisien. Lazy untuk array kecil?
Pat Niemeyer
@ PatNiemeyer: Saya tidak tahu, Anda harus mengukur kinerja dan membandingkan.
Martin R
@PatNiemeyer solusi di atas pasti akan lebih efisien walaupun perbedaannya mungkin tidak besar. 1. Kompleksitas filterselalu O(n)sedangkan di findFirstMatchingdalamnya hanya dalam skenario terburuk (ketika elemen yang Anda cari adalah yang terakhir atau tidak dalam array sama sekali). 2. filtermenciptakan array yang benar-benar baru dari elemen yang difilter sementara yang findFirstMatchingbaru saja mengembalikan elemen yang diminta.
0101
Di Swift 3, saya mendapatkan error Inheritance dari non-protocol, tipe non-class 'Bool' dan Penggunaan tipe 'T' yang tidak dideklarasikan untuk metode ekstensi ini.
Isuru
@ Isuru: Jawaban itu sudah cukup tua dan merujuk ke versi Swift yang lama. Di Swift 3 Anda tidak memerlukan metode ekstensi khusus lagi untuk tujuan itu, saya telah memperbarui jawabannya.
Martin R
20

Swift 3.0

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

Cepat 2.1

Pemfilteran dalam properti objek sekarang didukung di swift 2.1. Anda dapat memfilter array Anda berdasarkan nilai struct atau kelas di sini sebagai contoh

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

ATAU

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

Swift 4 ,

Cara lain untuk mencapai ini menggunakan fungsi filter,

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

Cepat 3

Anda dapat menggunakan indeks (di mana :) di Swift 3

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

contoh

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}
Deokhyun Ko
sumber
Apakah ada metode di swift 3 di mana Anda dapat menemukan indeks sublist dari item array yang memenuhi syarat $0.name == "Foo"?
Ahmed Khedr
3

Cepat 3

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

Cepat 2 atau lebih baru

Anda dapat menggabungkan indexOfdan mapmenulis fungsi "find element" dalam satu baris.

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

Menggunakan filter+ firstterlihat lebih bersih, tetapi filtermengevaluasi semua elemen dalam array. indexOf+ mapterlihat rumit, tetapi evaluasi berhenti ketika kecocokan pertama dalam array ditemukan. Kedua pendekatan memiliki pro dan kontra.

Yoichi Tagaya
sumber
1

Gunakan contains:

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

Atau Anda bisa mencoba apa yang Martin tunjukkan, di komentar dan filtercoba lagi: Temukan Objek dengan Properti di Array .

Kristen
sumber
Apakah itu hanya mengembalikan boolean? Saya perlu mendapatkan objek juga, tidak hanya memeriksa apakah ada dalam array.
Sahat Yalkabov
Anggapan itemini memiliki jenis yang sama dengan item dalam array. Namun, yang saya miliki hanyalah sebuah judul dari view.annotation.title. Saya perlu membandingkan item dalam array dengan judul ini.
Sahat Yalkabov
Sesuatu seperti if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov
Martin menunjukkan cara lain dalam komentar. Periksa tautan yang dia berikan.
Christian
1

Cara lain untuk mendapatkan akses ke array.index (dari: Any) adalah dengan mendeklarasikan objek Anda

import Foundation
class Model: NSObject {  }
gabi doroftei
sumber
1

Gunakan Dollar yang merupakan Lo-Dash atau Underscore.js untuk Swift:

import Dollar

let found = $.find(array) { $0.name == "Foo" }
Tyler Long
sumber
1

Swift 3:

Anda dapat menggunakan fungsionalitas bawaan Swift untuk menemukan objek khusus dalam Array.

Pertama, Anda harus memastikan objek kustom Anda sesuai dengan: Protokol yang setara .

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

Dengan fungsi Equatable yang ditambahkan ke objek Anda, Swift sekarang akan menunjukkan kepada Anda properti tambahan yang dapat Anda gunakan pada array:

//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
George Filippakos
sumber
1

Untuk Swift 3,

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

Misalnya, jika kami memiliki array angka:

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

Kami dapat menemukan nomor ganjil pertama seperti ini:

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

Itu akan mengirim kembali 4 sebagai bilangan bulat opsional, karena angka ganjil pertama (9) adalah pada indeks empat.

Amul4608
sumber