Ein Objekt im Array finden?

144

Hat Swift so etwas wie _.findWhere in Underscore.js?

Ich habe ein Array von Strukturen vom Typ Tund möchte überprüfen, ob das Array ein Strukturobjekt enthält, dessen nameEigenschaft gleich ist Foo.

Versucht zu verwenden find()und filter()aber sie funktionieren nur mit primitiven Typen, zB Stringoder Int. Wirft einen Fehler über die Nichteinhaltung des EquitableProtokolls oder ähnliches.

Sahat Yalkabov
quelle
Dies könnte das sein, wonach Sie suchen: Objekt mit Eigenschaft im Array suchen .
Martin R
warum konvertiert nicht zu NSDictionary und die Suche nach
Longbow
3
Ich glaube, find ist in Swift 2.0 nicht mehr verfügbar. Ich habe 1.2-Code in Swift 2.0 konvertiert und stattdessen IndexOf verwendet.
Swift Soda

Antworten:

91

FWIW, wenn Sie keine benutzerdefinierte Funktion oder Erweiterung verwenden möchten, können Sie:

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

Dies erzeugt dann namezuerst ein Arrayfind .

Wenn Sie ein großes Array haben, möchten Sie vielleicht Folgendes tun:

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

oder vielleicht:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}
Rintaro
quelle
Das ist noch besser. Ich werde dies als Antwort markieren, da es insgesamt einfacher aussieht und keine benutzerdefinierte Funktion erstellt werden muss.
Sahat Yalkabov
27
Ab Swift 2.0 können Sie verwenden: array.indexOf ({$ 0.name == "Foo"})
tf.alves
73
Ab Swift 3.0 können Sie: array.first (wobei: {$ 0.name == "Foo"}) verwenden, wenn Sie das Objekt benötigen
Brett
Wie kann ich überprüfen, ob $ 0.name die Zeichenfolge "foo" enthält? Bei den Antworten geht es um die genaue Übereinstimmung. Ich brauche enthält String. Kann jemand die Syntax dafür geben
Azik Abdullah
289

SWIFT 5

Überprüfen Sie, ob das Element vorhanden ist

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

Holen Sie sich das Element

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

Holen Sie sich das Element und seinen Versatz

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
}

Holen Sie sich den Offset

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}
Daniel
quelle
6
Schöne schnelle Antwort!
Zoltán
4
Vielen Dank, dass Sie mein Sparer sind. Dies hat meinen Code sehr bereinigt
AD Progress
Wie man mehrere Bedingungen überprüft, sagt, ich muss überprüfen, ob das Array $0.name == "foo"eine Operation und $0.name == "boo" eine andere Operation
ausführt
Dies hat mir mehr geholfen als die akzeptierte Antwort im Jahr 2020.
Psiloc
Wie kann ich überprüfen, ob $ 0.name die Zeichenfolge "foo" enthält? Kann jemand die Syntax dafür geben
Azik Abdullah
131

Sie können die indexfür Arrayein Prädikat verfügbare Methode verwenden ( siehe Apples Dokumentation hier ).

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

Für Ihr spezielles Beispiel wäre dies:

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]
}
user3799504
quelle
3
Ja, das funktioniert nur mit Swift 2.0. Entschuldigung, hätte es erwähnen sollen.
user3799504
@ user3799504 bedeutet $ 0?
Pramod Tapaniya
$ 0 ist eine Abkürzung für das erste Argument zum Abschluss. In diesem Fall bezieht es sich auf self.generator.element oder jedes Element des Arrays. Bitte sehen Sie: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504
1
Wie wäre es mit Swift 3?
Adnako
38

Swift 3

Wenn Sie das Objekt benötigen, verwenden Sie:

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

(Wenn Sie mehr als ein Objekt mit dem Namen "Foo" haben, firstwird das erste Objekt aus einer nicht angegebenen Reihenfolge zurückgegeben.)

Brett
quelle
Danke dafür. Das sollte weit oben sein!
NerdyTherapist
3
Das ist schön, danke! Kann auch so geschrieben werden:array.first {$0.name == "Foo"}
Roland T.
2
In Swift3 muss es seinarray.first(where: {$0.name == "Foo"})
Daniel
Mit Daniels Anmerkung ist dies DIE richtige, beste Antwort für Swift 3. Verwenden Sie keine Karte, sondern filtern Sie für diesen Zweck. Sie durchlaufen ganze Sammlungen, was sehr verschwenderisch sein kann.
Womble
20

Sie können das Array filtern und dann einfach das erste Element auswählen, wie unter Objekt mit Eigenschaft im Array suchen gezeigt .

Oder Sie definieren eine benutzerdefinierte Erweiterung

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

Anwendungsbeispiel:

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 können Sie die vorhandene first(where:)Methode verwenden (wie in einem Kommentar erwähnt ):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}
Martin R.
quelle
Wie ist dies in Bezug auf die Effizienz zu vergleichen array.lazy.filter( predicate ).first? Wie effizient ist .lazy für kleine Arrays?
Pat Niemeyer
@PatNiemeyer: Ich weiß nicht, man müsste die Leistung messen und vergleichen.
Martin R
@PatNiemeyer Die obige Lösung wird mit Sicherheit effizienter sein, auch wenn der Unterschied wahrscheinlich nicht groß sein wird. 1. Komplexität des filterist immer, O(n)während im findFirstMatchingnur das schlechteste Szenario ist (wenn das gesuchte Element das letzte ist oder überhaupt nicht im Array). 2. filtererstellt ein völlig neues Array gefilterter Elemente, während das findFirstMatchinggerade angeforderte Element zurückgegeben wird.
0101
In Swift 3 erhalte ich die Fehler Vererbung von Nicht-Protokoll-, Nicht-Klassentyp 'Bool' und Verwendung des nicht deklarierten Typs 'T' für diese Erweiterungsmethode.
Isuru
@Isuru: Diese Antwort war ziemlich alt und bezog sich auf eine alte Swift-Version. In Swift 3 benötigen Sie zu diesem Zweck keine benutzerdefinierte Erweiterungsmethode mehr. Ich habe die Antwort entsprechend aktualisiert.
Martin R
20

Swift 3.0

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

Swift 2.1

Das Filtern in Objekteigenschaften wird jetzt in Swift 2.1 unterstützt. Sie können Ihr Array basierend auf einem beliebigen Wert der Struktur oder Klasse filtern. Hier ist ein Beispiel

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

ODER

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

Swift 4 ,

Eine andere Möglichkeit, dies mithilfe der Filterfunktion zu erreichen:

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

Swift 3

Sie können index (where :) in Swift 3 verwenden

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

Beispiel

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}
Deokhyun Ko
quelle
Gibt es in Swift 3 eine Methode, mit der Sie die Indizes einer Unterliste von Array-Elementen finden können, die die Bedingung erfüllen $0.name == "Foo"?
Ahmed Khedr
3

Swift 3

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

Swift 2 oder höher

Sie können eine Funktion "Element suchen" in einer einzigen Zeile kombinieren indexOfund mapschreiben.

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

Die Verwendung von filter+ firstsieht sauberer aus, filterwertet jedoch alle Elemente im Array aus. indexOf+ mapsieht kompliziert aus, aber die Auswertung stoppt, wenn die erste Übereinstimmung im Array gefunden wird. Beide Ansätze haben Vor- und Nachteile.

Yoichi Tagaya
quelle
1

Verwendung contains:

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

Oder Sie können versuchen, worauf Martin Sie in den Kommentaren hingewiesen hat, und es erneut filterversuchen: Objekt mit Eigenschaft in Array suchen .

Christian
quelle
Wird das nur einen Booleschen Wert zurückgeben? Ich muss auch das Objekt erhalten und nicht nur prüfen, ob es sich im Array befindet.
Sahat Yalkabov
Diese Annahme entspricht itemdem Typ des Elements im Array. Ich habe jedoch nur einen Titel von view.annotation.title. Ich muss Elemente im Array anhand dieses Titels vergleichen.
Sahat Yalkabov
So etwas wie if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov
Martin hat dir in den Kommentaren einen anderen Weg gezeigt. Überprüfen Sie den Link, den er bereitgestellt hat.
Christian
1

Eine andere Möglichkeit, Zugriff auf array.index (von: Any) zu erhalten, besteht darin, Ihr Objekt zu deklarieren

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

Verwenden Sie für Swift Dollar , Lo-Dash oder Underscore.js:

import Dollar

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

Swift 3:

Sie können die in Swifts integrierte Funktionalität verwenden, um benutzerdefinierte Objekte in einem Array zu finden.

Zuerst müssen Sie sicherstellen, dass Ihr benutzerdefiniertes Objekt dem: Equatable-Protokoll entspricht .

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

Mit der Ihrem Objekt hinzugefügten Equatable-Funktionalität zeigt Swift jetzt zusätzliche Eigenschaften an, die Sie für ein Array verwenden können:

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

Für Swift 3

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

Wenn wir zum Beispiel ein Array von Zahlen hätten:

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

Wir konnten die erste ungerade Zahl wie folgt finden:

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

Dadurch wird 4 als optionale Ganzzahl zurückgesendet, da sich die erste ungerade Zahl (9) am Index vier befindet.

Amul4608
quelle