Znaleźć obiekt w tablicy?


144

Czy Swift ma coś takiego jak _.findWhere w Underscore.js?

Mam tablicę struktur typu Ti chciałbym sprawdzić, czy tablica zawiera obiekt struct, którego namewłaściwość jest równa Foo.

Próbował użyć find()i filter()ale tylko pracę z prymitywnych typów, np Stringlub Int. Zgłasza błąd o niezgodności z Equitableprotokołem lub coś w tym rodzaju.


Może to być to, czego szukasz: Znajdź obiekt z właściwością w tablicy .
Martin R

dlaczego nie przekonwertować na nsdictionary i wyszukać
longbow

3
Wydaje mi się, że find nie jest już dostępny w Swift 2.0. Konwertowałem kod 1.2 do Swift 2.0 i powiedział, że zamiast tego używam IndexOf.
Swift Soda

Odpowiedzi:


91

FWIW, jeśli nie chcesz używać niestandardowej funkcji lub rozszerzenia, możesz:

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

To generuje namenajpierw tablicę, a następnie findz niej.

Jeśli masz ogromną tablicę, możesz to zrobić:

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

albo może:

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

To jest jeszcze lepsze. Oznaczę to jako odpowiedź, ponieważ ogólnie wygląda na prostszą i nie wymaga tworzenia niestandardowej funkcji.
Sahat Yalkabov

27
Od wersji Swift 2.0 możesz użyć: array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
Od wersji Swift 3.0 możesz użyć: array.first (gdzie: {$ 0.name == "Foo"}), jeśli potrzebujesz obiektu
Brett,

Jak mogę sprawdzić, czy $ 0.name zawiera ciąg „foo”? odpowiedzi dotyczą dokładnego dopasowania. Potrzebuję zawiera ciąg. Czy ktoś może podać składnię tego
Azik Abdullah

289

SWIFT 5

Sprawdź, czy element istnieje

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

Zdobądź element

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

Pobierz element i jego przesunięcie

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
}

Uzyskaj wyrównanie

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

6
Niezła odpowiedź w stylu Swift!
Zoltán

4
Dzięki, jesteś moim oszczędzającym, to bardzo wyczyściło mój kod
AD Progress,

jak sprawdzić wiele warunków powiedzieć, muszę sprawdzić, czy tablica $0.name == "foo"zrobić jedną operację i $0.name == "boo" zrobić kolejną operację
Midhun Narayan

Pomogło mi to bardziej niż zaakceptowana odpowiedź w 2020 r.
Psiloc

Jak mogę sprawdzić, czy $ 0.name zawiera ciąg „foo”? Czy ktoś może podać składnię tego
Azik Abdullah

131

Możesz użyć indexmetody dostępnej w Arraypredykacie ( zobacz dokumentację Apple tutaj ).

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

Dla twojego konkretnego przykładu będzie to:

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
Tak, działa tylko ze Swift 2.0. Przepraszam, że powinienem o tym wspomnieć.
user3799504

@ user3799504 oznacza $ 0?
Pramod Tapaniya

$ 0 jest skrótem dla pierwszego argumentu zamknięcia. W tym przypadku odnosi się do self.generator.element lub do każdego elementu tablicy. Zobacz: developer.apple.com/library/ios/documentation/Swift/Conceptual/ ...
user3799504

1
A co z Swift 3?
adnako

38

Szybki 3

Jeśli potrzebujesz obiektu użyj:

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

(Jeśli masz więcej niż jeden obiekt o nazwie „Foo” first, zwróci pierwszy obiekt z nieokreślonej kolejności)


Dziękuję Ci za to. To powinno być wysoko!
NerdyTherapist

3
To miłe, dziękuję! Można też tak napisać:array.first {$0.name == "Foo"}
Roland T.

2
W Swift3 musi to byćarray.first(where: {$0.name == "Foo"})
Daniel

Z uwagą Daniela jest to NAJLEPSZA, najlepsza odpowiedź dla Swift 3. Nie używaj mapy, filtruj do tego celu; iterują po całych kolekcjach, co może być ogromnym marnotrawstwem.
Womble

20

Możesz przefiltrować tablicę, a następnie po prostu wybrać pierwszy element, jak pokazano w Znajdź obiekt z właściwością w tablicy .

Możesz też zdefiniować rozszerzenie niestandardowe

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

Przykład użycia:

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
}

W Swift 3 możesz użyć istniejącej first(where:)metody (jak wspomniano w komentarzu ):

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

Pod względem wydajności, z czym to się wypada array.lazy.filter( predicate ).first? Jak wydajne jest .lazy w przypadku małych tablic?
Pat Niemeyer

@PatNiemeyer: Nie wiem, musiałbyś mierzyć wydajność i porównywać.
Martin R

@PatNiemeyer powyższe rozwiązanie z pewnością będzie bardziej wydajne, chociaż różnica prawdopodobnie nie będzie duża. 1. Złożoność filterjest zawsze, O(n)podczas gdy w przypadku findFirstMatchingjest to tylko w najgorszym scenariuszu (gdy element, którego szukasz, jest ostatnim w tablicy lub nie ma go wcale). 2.filter tworzy zupełnie nową tablicę przefiltrowanych elementów, podczas gdy ten findFirstMatchingwłaśnie zwraca żądany element.
0101

W Swift 3 otrzymuję błędy Dziedziczenie z nieprotokołowego, nieklasowego typu „Bool” i Użycie niezadeklarowanego typu „T” dla tej metody rozszerzenia.
Isuru,

@Isuru: Ta odpowiedź była dość stara i odnosiła się do starej wersji Swift. W Swift 3 nie potrzebujesz już niestandardowej metody rozszerzenia do tego celu, odpowiednio zaktualizowałem odpowiedź.
Martin R

20

Swift 3.0

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

Swift 2.1

Filtrowanie we właściwościach obiektu jest teraz obsługiwane w Swift 2.1. Możesz filtrować swoją tablicę na podstawie dowolnej wartości struktury lub klasy. Oto przykład

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

LUB

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

9

Szybki 4 ,

Inny sposób na osiągnięcie tego za pomocą funkcji filtra,

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

8

Szybki 3

możesz użyć indeksu (gdzie :) w Swift 3

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

przykład

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

Czy w Swift 3 jest metoda, w której można znaleźć indeksy podlisty elementów tablicy, które spełniają warunek $0.name == "Foo"?
Ahmed Khedr

3

Szybki 3

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

2

Swift 2 lub nowszy

Możesz łączyć indexOfi mappisać funkcję „znajdź element” w jednym wierszu.

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

Używanie filter+ firstwygląda czyściej, ale filterocenia wszystkie elementy tablicy. indexOf+ mapwygląda na skomplikowane, ale ocena kończy się, gdy zostanie znalezione pierwsze dopasowanie w tablicy. Oba podejścia mają zalety i wady.


1

Zastosowanie contains:

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

Możesz też wypróbować to, na co wskazał cię Martin, w komentarzach i filterspróbować jeszcze raz: Znajdź obiekt z właściwością w tablicy .


Czy to zwróci tylko wartość logiczną? Muszę też pobrać obiekt, a nie tylko sprawdzić, czy znajduje się w tablicy.
Sahat Yalkabov

To założenie itemjest tego samego typu co element tablicy. Jednak mam tylko tytuł z view.annotation.title. Muszę porównać elementy w tablicy według tego tytułu.
Sahat Yalkabov

Coś jak if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov

Martin pokazał ci inny sposób w komentarzach. Sprawdź podany przez niego link.
Christian,

1

Innym sposobem uzyskania dostępu do array.index (of: Any) jest zadeklarowanie obiektu

import Foundation
class Model: NSObject {  }

1

Użyj Dollar, którym jest Lo-Dash lub Underscore.js dla Swift:

import Dollar

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

1

Swift 3:

Możesz użyć wbudowanej funkcji jerzyków, aby znaleźć niestandardowe obiekty w tablicy.

Najpierw musisz upewnić się, że Twój obiekt niestandardowy jest zgodny z protokołem : 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)
    }
}

Po dodaniu funkcji Equatable do Twojego obiektu Swift pokaże Ci dodatkowe właściwości, których możesz użyć w tablicy:

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


0

Na przykład, gdybyśmy mieli tablicę liczb:

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

Możemy znaleźć pierwszą liczbę nieparzystą w ten sposób:

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

To zwróci 4 jako opcjonalną liczbę całkowitą, ponieważ pierwsza liczba nieparzysta (9) ma indeks czwarty.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.