Trova un oggetto nella matrice?


144

Swift ha qualcosa come _.findWhere in Underscore.js?

Ho una serie di strutture di tipo Te vorrei verificare se la matrice contiene un oggetto struct la cui nameproprietà è uguale a Foo.

Ho provato a usare find()e filter()ma funzionano solo con tipi primitivi, ad esempio Stringo Int. Genera un errore in caso di non conformità al Equitableprotocollo o qualcosa del genere.


Questo potrebbe essere quello che stai cercando: Trova oggetto con proprietà nell'array .
Martin R,

perché non convertire in nsdictionary e cercare
longbow il

3
Credo che find non sia più disponibile in Swift 2.0 .. Stavo convertendo un po 'di codice 1.2 in Swift 2.0 e invece diceva di usare IndexOf.
Swift Soda,

Risposte:


91

FWIW, se non si desidera utilizzare la funzione o l'estensione personalizzata, è possibile:

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

Questo genera nameprima l'array, quindi findda esso.

Se hai un array enorme, potresti voler fare:

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

o forse:

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

È ancora meglio Contrassegnerò questo come risposta poiché sembra complessivamente più semplice e non richiede la creazione di una funzione personalizzata.
Sahat Yalkabov, il

27
A partire da Swift 2.0 è possibile utilizzare: array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
A partire da Swift 3.0 puoi usare: array.first (dove: {$ 0.name == "Foo"}) se hai bisogno dell'oggetto
Brett

Come posso controllare $ 0.name contiene la stringa "pippo"? le risposte riguardano la corrispondenza esatta. Ho bisogno contiene stringa. Qualcuno può dare la sintassi per questo
Azik Abdullah

290

SWIFT 5

Controlla se l'elemento esiste

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

Ottieni l'elemento

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

Ottieni l'elemento e il suo offset

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
}

Ottieni l'offset

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

6
Bella risposta in stile Swift!
Zoltán,

4
Grazie sei il mio risparmiatore questo ha ripulito molto il mio codice
Progresso AD

come controllare più condizioni dire che $0.name == "foo"$0.name == "boo"
devo

Questo mi ha aiutato più della risposta accettata nel 2020.
Psiloc,

Come posso controllare $ 0.name contiene la stringa "pippo"? Qualcuno può dare la sintassi per questo
Azik Abdullah

131

Puoi usare il indexmetodo disponibile Arraycon un predicato ( vedi la documentazione di Apple qui ).

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

Per il tuo esempio specifico questo sarebbe:

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
Sì, funziona solo con Swift 2.0. Scusa, avrei dovuto parlarne.
user3799504,

@ user3799504 che significa $ 0?
Pramod Tapaniya,

$ 0 è una scorciatoia per il primo argomento alla chiusura. In questo caso si riferisce a self.generator.element o a ciascun elemento dell'array. Si prega di consultare: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504

1
Che ne dici di Swift 3?
adnako,

38

Swift 3

Se hai bisogno dell'oggetto usa:

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

(Se si dispone di più di un oggetto denominato "Foo", firstverrà restituito il primo oggetto da un ordine non specificato)


Grazie per questo. Questo dovrebbe essere lassù!
NerdyTherapist,

3
Questo è carino, grazie! Può essere scritto anche in questo modo:array.first {$0.name == "Foo"}
Roland T.

2
In Swift3 deve esserearray.first(where: {$0.name == "Foo"})
Daniel,

Con la nota di Daniel, questa è LA risposta migliore e corretta per Swift 3. Non usare la mappa, filtra per questo scopo; iterano su intere collezioni, che possono essere estremamente dispendiose.
Womble,

20

È possibile filtrare l'array e quindi selezionare il primo elemento, come mostrato in Trova oggetto con proprietà nell'array .

Oppure si definisce un'estensione personalizzata

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

Esempio di utilizzo:

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 puoi utilizzare il first(where:)metodo esistente (come indicato in un commento ):

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

In termini di efficienza, come si confronta con questo array.lazy.filter( predicate ).first? Quanto è efficiente .lazy per piccoli array?
Pat Niemeyer,

@PatNiemeyer: Non lo so, dovresti misurare le prestazioni e confrontarle.
Martin R

@PatNiemeyer la soluzione sopra sarà sicuramente più efficiente anche se la differenza non sarà probabilmente grande. 1. La complessità di filterè sempre O(n)mentre in findFirstMatchingè solo nello scenario peggiore (quando l'elemento che stai cercando è l'ultimo o no nell'array). 2. filtercrea un array completamente nuovo di elementi filtrati mentre findFirstMatchingjust restituisce l'elemento richiesto.
0101

In Swift 3, sto ricevendo gli errori Ereditarietà da tipo non protocollo, non di classe "Bool" e Uso del tipo non dichiarato "T" per questo metodo di estensione.
Isuru,

@Isuru: quella risposta era piuttosto vecchia e si riferiva a una vecchia versione di Swift. In Swift 3 non hai più bisogno di un metodo di estensione personalizzato a tale scopo, ho aggiornato la risposta di conseguenza.
Martin R

20

Swift 3.0

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

Swift 2.1

Il filtro nelle proprietà degli oggetti è ora supportato in swift 2.1. È possibile filtrare l'array in base a qualsiasi valore della struttura o della classe, qui è un esempio

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

O

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

9

Swift 4 ,

Un altro modo per raggiungere questo obiettivo utilizzando la funzione filtro,

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

8

Swift 3

puoi usare index (dove :) in Swift 3

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

esempio

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

Esiste un metodo in Swift 3 in cui è possibile trovare gli indici di un elenco secondario di elementi dell'array che soddisfano la condizione $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)
}

2

Swift 2 o successivo

È possibile combinare indexOfe mapscrivere una funzione "trova elemento" in un'unica riga.

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

L'uso di filter+ firstsembra più pulito, ma filtervaluta tutti gli elementi dell'array. indexOf+ mapsembra complicato, ma la valutazione si interrompe quando viene trovata la prima corrispondenza nella matrice. Entrambi gli approcci hanno pro e contro.


1

Utilizzare contains:

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

Oppure potresti provare ciò che Martin ti ha indicato, nei commenti e fare filterun altro tentativo: Trova oggetto con proprietà in array .


Restituirà solo un booleano? Devo anche ottenere l'oggetto, non solo controllare se si trova nell'array.
Sahat Yalkabov,

Questo presuppone itemsia dello stesso tipo dell'articolo nell'array. Tuttavia, tutto quello che ho è solo un titolo di view.annotation.title. Ho bisogno di confrontare gli oggetti nella matrice con questo titolo.
Sahat Yalkabov,

Qualcosa del genere if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov,

Martin ti ha mostrato un altro modo nei commenti. Controlla il link che ha fornito.
Christian,

1

Un altro modo per ottenere l'accesso a array.index (di: Any) è dichiarare l'oggetto

import Foundation
class Model: NSObject {  }

1

Usa Dollar che è Lo-Dash o Underscore.js per Swift:

import Dollar

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

1

Swift 3:

È possibile utilizzare la funzionalità integrata Swift per trovare oggetti personalizzati in un array.

Innanzitutto è necessario assicurarsi che l'oggetto personalizzato sia conforme al protocollo : Equatable .

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

Con la funzionalità Equatable aggiunta al tuo oggetto, Swift ora ti mostrerà proprietà aggiuntive che puoi usare su un 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

1

Per Swift 3,

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

0

Ad esempio, se avessimo una matrice di numeri:

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

Potremmo trovare il primo numero dispari come questo:

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

Ciò restituirà 4 come numero intero opzionale, poiché il primo numero dispari (9) è all'indice quattro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.