Dizideki bir nesneyi mi buldunuz?


144

Swift, Underscore.js'de _.findWhere gibi bir şeye sahip mi?

Tür yapıları bir dizi var ve dizi özelliği eşit Tbir yapı nesnesi içerip içermediğini denetlemek istiyorum .nameFoo

Kullanmaya çalıştım find()ve filter()sadece ilkel tiplerle çalışıyorlar, örneğin Stringveya Int. EquitableProtokole veya buna benzer bir şeye uymama konusunda bir hata atar .


Aradığın şey bu olabilir: Array'da Özellikli Nesne Bul .
Martin R

neden nsdictionary dönüştürmek ve arama
longbow

3
Ben artık Swift 2.0 kullanılabilir bulmak inanıyorum .. Swift 2.0 için bazı 1.2 kod dönüştürüyordum ve bunun yerine IndexOf kullanmak dedi.
Swift Soda

Yanıtlar:


91

FWIW, özel işlev veya uzantı kullanmak istemiyorsanız şunları yapabilirsiniz:

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

Bu nameönce diziyi sonra da diziyi oluşturur find.

Büyük bir diziniz varsa, şunları yapmak isteyebilirsiniz:

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

ya da belki:

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

Bu daha iyi. Genel olarak daha basit göründüğü ve özel bir işlev oluşturmayı gerektirmediği için bunu cevap olarak işaretleyeceğim.
Sahat Yalkabov

27
Swift 2.0'dan itibaren şunları kullanabilirsiniz: array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
Swift 3.0'dan itibaren şunları kullanabilirsiniz: array.first (burada: {$ 0.name == "Foo"})
Brett

$ 0.name "foo" dizesini içeriyorsa nasıl kontrol edebilirim? cevaplar tam eşleme ile ilgilidir. Ben dize içerir gerekir. Herkes bu sözdizimini verebilir
Azik Abdullah

290

SWIFT 5

Öğenin var olup olmadığını kontrol edin

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

Elemanı al

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

Elemanı ve ofsetini alı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
}

Ofseti alın

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

6
Güzel Swift tarzı cevap!
Zoltán

4
Teşekkürler koruyucu benim bu kodumu çok temizledi
AD İlerleme

nasıl birden fazla koşul kontrol dizinin $0.name == "foo"bir işlem $0.name == "boo" yapmak ve başka bir işlem yapmak gerektiğini kontrol etmek gerektiğini söylüyorlar
Midhun Narayan

Bu bana 2020'de kabul edilen cevaptan daha fazla yardımcı oldu.
Psiloc

$ 0.name "foo" dizesini içeriyorsa nasıl kontrol edebilirim? Herkes bu sözdizimini verebilir
Azik Abdullah

132

Sen kullanabilirsiniz indexgeçerli yöntem Array(bir yüklemi burada Apple'ın belgelerine bakın ).

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

Özel örneğiniz için bu şöyle olur:

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
Evet, bu sadece swift 2.0 ile çalışır. Üzgünüm bundan bahsetmeliydim.
user3799504

@ user3799504 $ 0 demek?
Pramod Tapaniya

$ 0, kapatmanın ilk argümanı için kısayol. Bu durumda, self.generator.element veya dizinin her bir öğesini ifade eder. Lütfen bakınız: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504

1
Swift 3'e ne dersin?
adnako

38

Hızlı 3

Nesne kullanımına ihtiyacınız varsa:

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

("Foo" adında birden fazla nesneniz varsa first, ilk nesneyi belirtilmemiş bir siparişten döndürür)


Bunun için teşekkür ederim. Bu orada olmalı!
NerdyTherapist

3
Bu güzel, teşekkürler! Bu şekilde de yazılabilir:array.first {$0.name == "Foo"}
Roland T.

2
In swift3 öyle olmalıarray.first(where: {$0.name == "Foo"})
Daniel

Daniel'in notuna göre, bu Swift 3 için doğru, en iyi cevaptır. Bu amaçla harita kullanmayın, filtreleyin; büyük bir israfa yol açabilecek tüm koleksiyonları yineliyorlar.
Womble

20

Sen edebilir filtre dizisi ve gösterildiği gibi sonra sadece birinci elemanı almak Array Mülkü'yle Bul Nesne .

Veya özel bir uzantı tanımlarsınız

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

Kullanım örneği:

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
}

In Swift 3 varolan kullanabilirsiniz first(where:)yöntemi (aynı Bir yorumda ):

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

Verimlilik açısından, bu nasıldır array.lazy.filter( predicate ).first? Küçük diziler için .lazy ne kadar etkilidir?
Pat Niemeyer

@PatNiemeyer: Bilmiyorum, performansı ölçmek ve karşılaştırmak zorunda kalacaksınız.
Martin R

@PatNiemeyer yukarıdaki çözüm büyük olasılıkla büyük olmasa bile kesinlikle daha verimli olacaktır. 1. Karmaşıklık filterdaima O(n)oysa içinde findFirstMatchingen kötü senaryoda sadece var (Aradığınız elemanı hiç dizideki son ya olmadığında). 2. sadece istenen öğeyi döndürürken filtertamamen yeni bir filtre uygulanmış öğe dizisi oluşturur findFirstMatching.
0101

Swift 3'te, bu uzantı yöntemi için protokol olmayan, sınıf dışı tip 'Bool' ve bildirilmemiş tip 'T' kullanımı Kalıtım hatalarını alıyorum .
Isuru

@Isuru: Bu cevap oldukça eskiydi ve eski bir Swift versiyonuna atıfta bulundu. Swift 3'te artık bu amaçla özel bir uzantı yöntemine ihtiyacınız yok, cevabı buna göre güncelledim.
Martin R

20

Swift 3.0

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

Hızlı 2.1

Nesne özelliklerinde filtreleme artık hızlı 2.1'de destekleniyor. Dizinizi yapının veya sınıfın herhangi bir değerine göre filtreleyebilirsiniz burada bir örnek

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

VEYA

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

9

Swift 4 ,

Filtre işlevini kullanarak bunu başarmanın başka bir yolu,

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

8

Hızlı 3

Swift 3'te index (where :) kullanabilirsiniz

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

misal

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

Hızlı 3'te koşulu karşılayan dizi öğelerinin bir alt listesinin dizinlerini bulabileceğiniz bir yöntem var mı $0.name == "Foo"?
Ahmed Khedr

3

Hızlı 3

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

2

Swift 2 veya üstü

Bir araya getirebilir indexOfve map"bul eleman" fonksiyonunu tek bir satıra yazabilirsiniz.

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+ Kullanımı firstdaha temiz görünür, ancak filterdizideki tüm öğeleri değerlendirir. indexOf+ mapkarmaşık görünüyor, ancak dizideki ilk eşleşme bulunduğunda değerlendirme durur. Her iki yaklaşımın da artıları ve eksileri vardır.


1

Kullanım contains:

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

Veya yorumlarda Martin'in işaret ettiği şeyi filterdeneyebilir ve başka bir deneme yapabilirsiniz: Array'da Mülkiyetli Nesne Bul .


Bu sadece bir boole döndürecek mi? Ben de sadece dizi olup olmadığını kontrol değil, nesne almak gerekir.
Sahat Yalkabov

Bu varsayım item, dizideki öğeyle aynı türdedir. Ancak, sahip olduğum tek şey sadece bir başlık view.annotation.title. Bu başlık ile dizideki öğeleri karşılaştırmak gerekiyor.
Sahat Yalkabov

Gibi bir şey if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov

Martin yorumlarda sana başka bir yol gösterdi. Sağladığı bağlantıyı kontrol edin.
Christian

1

Array.index'e (of: Any) erişmenin başka bir yolu da nesnenizi bildirmektir

import Foundation
class Model: NSObject {  }

1

Swift için Lo-Dash veya Underscore.js olan Dolar'ı kullanın :

import Dollar

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

1

Hızlı 3:

Bir Dizideki özel nesneleri bulmak için yerleşik işlevsellikleri kullanabilirsiniz.

Öncelikle özel nesnenizin: Equatable protokolüne uygun olduğundan emin olmalısınız .

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

Nesnenize Eşitlenebilir işlevsellik eklendiğinde, Swift artık bir dizide kullanabileceğiniz ek özellikler gösterecek:

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

Swift 3 için,

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

0

Örneğin, bir dizi sayımız olsaydı:

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

İlk tek sayıyı şu şekilde bulabiliriz:

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

Bu, 4'ü isteğe bağlı bir tamsayı olarak geri gönderir, çünkü ilk tek sayı (9) dördüncü dizindedir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.