Trouver un objet dans un tableau?

144

Swift a-t-il quelque chose comme _.findWhere dans Underscore.js?

J'ai un tableau de structures de type Tet je voudrais vérifier si array contient un objet struct dont la namepropriété est égale à Foo.

J'ai essayé d'utiliser find()et filter()mais ils ne fonctionnent qu'avec des types primitifs, par exemple Stringou Int. Lève une erreur concernant le non-respect du Equitableprotocole ou quelque chose comme ça.

Sahat Yalkabov
la source
Cela pourrait être ce que vous recherchez: Rechercher un objet avec une propriété dans un tableau .
Martin R
pourquoi ne pas convertir en nsdictionary et rechercher
longbow
3
Je crois que find n'est plus disponible dans Swift 2.0 .. J'étais en train de convertir du code 1.2 en Swift 2.0 et il a dit d'utiliser IndexOf à la place.
Swift Soda

Réponses:

91

FWIW, si vous ne souhaitez pas utiliser de fonction ou d'extension personnalisée, vous pouvez:

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

Cela génère named'abord le tableau, puis à findpartir de celui-ci.

Si vous avez un énorme tableau, vous pouvez faire:

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

ou peut-être:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}
Rintaro
la source
C'est encore mieux. Je marquerai ceci comme réponse car cela semble plus simple dans l'ensemble et ne nécessite pas de créer une fonction personnalisée.
Sahat Yalkabov le
27
Depuis Swift 2.0, vous pouvez utiliser: array.indexOf ({$ 0.name == "Foo"})
tf.alves
73
Depuis Swift 3.0, vous pouvez utiliser: array.first (où: {$ 0.name == "Foo"}) si vous avez besoin de l'objet
Brett
Comment puis-je vérifier que $ 0.name contient la chaîne "foo"? les réponses concernent la correspondance exacte. J'ai besoin de contenir une chaîne. Quelqu'un peut-il donner la syntaxe pour cela
Azik Abdullah
290

SWIFT 5

Vérifier si l'élément existe

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

Obtenez l'élément

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

Obtenir l'élément et son décalage

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
}

Obtenez le décalage

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}
Daniel
la source
6
Belle réponse de style Swift!
Zoltán
4
Merci, vous êtes mon économiseur, cela a beaucoup nettoyé mon code
AD Progress
comment vérifier plusieurs conditions disent que je dois vérifier si la baie $0.name == "foo"effectue une opération et $0.name == "boo" une autre opération
Midhun Narayan
Cela m'a aidé plus que la réponse acceptée en 2020.
Psiloc
Comment puis-je vérifier que $ 0.name contient la chaîne "foo"? Quelqu'un peut-il donner la syntaxe pour cela
Azik Abdullah
131

Vous pouvez utiliser la indexméthode disponible sur Arrayavec un prédicat ( voir la documentation Apple ici ).

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

Pour votre exemple spécifique, ce serait:

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
la source
3
Oui, cela fonctionne uniquement avec Swift 2.0. Désolé aurait dû le mentionner.
user3799504
@ user3799504 signifie 0 $?
Pramod Tapaniya
$ 0 est un raccourci pour le premier argument de la fermeture. Dans ce cas, il fait référence à self.generator.element ou à chaque élément du tableau. Veuillez consulter: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504
1
Et pour Swift 3?
adnako
38

Swift 3

Si vous avez besoin de l'objet, utilisez:

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

(Si vous avez plus d'un objet nommé "Foo" first, le premier objet retournera d'un ordre non spécifié)

Brett
la source
Merci pour ça. Cela devrait être là-haut!
NerdyTherapist
3
C'est sympa, merci! Peut être écrit comme ceci aussi:array.first {$0.name == "Foo"}
Roland T.
2
Dans Swift3 ça doit êtrearray.first(where: {$0.name == "Foo"})
Daniel
Avec la note de Daniel, c'est LA meilleure réponse correcte pour Swift 3. N'utilisez pas de carte, filtrez à cet effet; ils parcourent des collections entières, ce qui peut être un gaspillage énorme.
Womble
20

Vous pouvez filtrer le tableau, puis choisir simplement le premier élément, comme indiqué dans Rechercher un objet avec une propriété dans un tableau .

Ou vous définissez une extension personnalisée

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

Exemple d'utilisation:

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
}

Dans Swift 3, vous pouvez utiliser la first(where:)méthode existante (comme mentionné dans un commentaire ):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}
Martin R
la source
En termes d'efficacité, comment cela se compare- array.lazy.filter( predicate ).firstt-il? Quelle est l'efficacité de .lazy pour les petits tableaux?
Pat Niemeyer
@PatNiemeyer: Je ne sais pas, il faudrait mesurer les performances et comparer.
Martin R
@PatNiemeyer la solution ci-dessus sera très certainement plus efficace même si la différence ne sera probablement pas grande. 1. La complexité du filterest toujours O(n)alors que dans le findFirstMatchingc'est seulement dans le pire des scénarios (lorsque l'élément que vous recherchez est le dernier ou pas du tout dans le tableau). 2. filtercrée un tout nouveau tableau d'éléments filtrés tandis que le findFirstMatchingrenvoie simplement l'élément demandé.
0101
Dans Swift 3, j'obtiens les erreurs Héritage du type non-protocole, non-classe 'Bool' et Utilisation du type non déclaré 'T' pour cette méthode d'extension.
Isuru
@Isuru: Cette réponse était assez ancienne et faisait référence à une ancienne version de Swift. Dans Swift 3, vous n'avez plus besoin d'une méthode d'extension personnalisée à cette fin, j'ai mis à jour la réponse en conséquence.
Martin R
20

Swift 3.0

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

Swift 2.1

Le filtrage dans les propriétés des objets est désormais pris en charge dans swift 2.1. Vous pouvez filtrer votre tableau en fonction de n'importe quelle valeur de la structure ou de la classe, voici un exemple

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

OU

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

Swift 4 ,

Une autre façon d'y parvenir en utilisant la fonction de filtre,

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

Swift 3

vous pouvez utiliser index (où :) dans Swift 3

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

exemple

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}
Deokhyun Ko
la source
Existe-t-il une méthode dans swift 3 où vous pouvez trouver les indices d'une sous-liste d'éléments de tableau qui remplissent la condition $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
la source
2

Swift 2 ou version ultérieure

Vous pouvez combiner indexOfet mapécrire une fonction "find element" sur une seule ligne.

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'utilisation de filter+ firstsemble plus propre, mais filterévalue tous les éléments du tableau. indexOf+ mapsemble compliqué, mais l'évaluation s'arrête lorsque la première correspondance du tableau est trouvée. Les deux approches ont des avantages et des inconvénients.

Yoichi Tagaya
la source
1

Utilisez contains:

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

Ou vous pouvez essayer ce que Martin vous a indiqué dans les commentaires et filterréessayer: Rechercher un objet avec une propriété dans un tableau .

Christian
la source
Est-ce que cela ne renverra qu'un booléen? J'ai besoin d'obtenir l'objet aussi, pas seulement de vérifier s'il se trouve dans le tableau.
Sahat Yalkabov
Cette hypothèse itemest du même type que l'élément du tableau. Cependant, tout ce que j'ai est juste un titre view.annotation.title. J'ai besoin de comparer les éléments du tableau par ce titre.
Sahat Yalkabov
Quelque chose comme if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov
Martin vous a montré une autre manière dans les commentaires. Vérifiez le lien qu'il a fourni.
Christian
1

Une autre façon d'accéder à array.index (of: Any) est de déclarer votre objet

import Foundation
class Model: NSObject {  }
gabi doroftei
la source
1

Utilisez Dollar qui est Lo-Dash ou Underscore.js pour Swift:

import Dollar

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

Swift 3:

Vous pouvez utiliser la fonctionnalité intégrée Swifts pour rechercher des objets personnalisés dans un tableau.

Vous devez d'abord vous assurer que votre objet personnalisé est conforme au protocole : 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)
    }
}

Avec la fonctionnalité Equatable ajoutée à votre objet, Swift vous montrera désormais des propriétés supplémentaires que vous pouvez utiliser sur un tableau:

//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
la source
1

Pour Swift 3,

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

Par exemple, si nous avions un tableau de nombres:

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

Nous pourrions trouver le premier nombre impair comme ceci:

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

Cela renverra 4 comme entier facultatif, car le premier nombre impair (9) est à l'index quatre.

Amul4608
la source