Encontre um objeto na matriz?

144

O Swift tem algo como _.findWhere in Underscore.js?

Eu tenho uma matriz de estruturas do tipo Te gostaria de verificar se a matriz contém um objeto struct cuja namepropriedade é igual a Foo.

Tentou usar find()e, filter()mas eles só funcionam com tipos primitivos, por exemplo, Stringou Int. Lança um erro sobre não estar em conformidade com o Equitableprotocolo ou algo assim.

Sahat Yalkabov
fonte
Pode ser o que você está procurando: Localizar objeto com propriedade na matriz .
Martin R
por que não converter para nsdictionary e procurar
longbow
3
Eu acredito que o find não está mais disponível no Swift 2.0. Eu estava convertendo um código 1.2 para o Swift 2.0 e ele disse que usava IndexOf.
Swift Soda

Respostas:

91

FWIW, se você não quiser usar a função ou extensão personalizada, poderá:

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

Isso gera o namearray primeiro e depois a findpartir dele.

Se você tiver uma variedade enorme, convém:

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

ou talvez:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}
rintaro
fonte
Isto é ainda melhor. Marcarei isso como resposta, pois parece mais simples no geral e não requer a criação de uma função personalizada.
Sahat Yalkabov
27
A partir do Swift 2.0, você pode usar: array.indexOf ({$ 0.name == "Foo"})
tf.alves
73
A partir do Swift 3.0, você pode usar: array.first (where: {$ 0.name == "Foo"}) se você precisar do objeto
Brett
Como posso verificar que $ 0.name contém a string "foo"? as respostas são sobre correspondência exata. Eu preciso contém string. Alguém pode dar a sintaxe para isso
Azik Abdullah
290

SWIFT 5

Verifique se o elemento existe

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

Obter o elemento

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

Obter o elemento e seu deslocamento

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
}

Obter o deslocamento

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}
Daniel
fonte
6
Ótima resposta no estilo Swift!
Zoltán
4
Graças você é meu protetor isso tem limpo o meu código muito
AD Progress
como verificar várias condições dizer que eu preciso verificar se a matriz $0.name == "foo"fazer uma operação e $0.name == "boo" fazer outra operação
Midhun Narayan
Isso me ajudou mais do que a resposta aceita em 2020.
Psiloc 26/03
Como posso verificar que $ 0.name contém a string "foo"? Alguém pode dar a sintaxe para isso
Azik Abdullah
131

Você pode usar o indexmétodo disponível Arraycom um predicado ( consulte a documentação da Apple aqui ).

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

Para o seu exemplo específico, isso seria:

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
fonte
3
Sim, isso funciona apenas com o Swift 2.0. Desculpe, deveria ter mencionado.
User3799504
@ user3799504 significando $ 0?
Pramod Tapaniya 17/03/16
$ 0 é uma abreviação para o primeiro argumento para o fechamento. Nesse caso, refere-se a self.generator.element ou a cada elemento da matriz. Por favor, consulte: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504
1
E o Swift 3?
Adnako 31/05
38

Swift 3

Se você precisar do objeto, use:

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

(Se você tiver mais de um objeto chamado "Foo", firstretornará o primeiro objeto de uma ordem não especificada)

Brett
fonte
Obrigado por isso. Isso deve estar lá em cima!
precisa saber é o seguinte
3
Isso é legal, obrigado! Também pode ser escrito assim:array.first {$0.name == "Foo"}
Roland T.
2
Em Swift3 ele deve serarray.first(where: {$0.name == "Foo"})
Daniel
Com a nota de Daniel, esta é a melhor resposta correta para o Swift 3. Não use o mapa, filtre para esse fim; eles repetem coleções inteiras, o que pode ser um enorme desperdício.
Womble
20

Você pode filtrar a matriz e, em seguida, basta escolher o primeiro elemento, conforme mostrado em Localizar objeto com propriedade na matriz .

Ou você define uma extensão personalizada

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

Exemplo de uso:

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
}

No Swift 3, você pode usar o first(where:)método existente (conforme mencionado em um comentário ):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}
Martin R
fonte
Em termos de eficiência, como isso se compara array.lazy.filter( predicate ).first? Qual a eficiência do .lazy para pequenas matrizes?
Pat Niemeyer
@ PatNiemeyer: Eu não sei, você precisaria medir o desempenho e comparar.
Martin R
@PatNiemeyer, a solução acima certamente será mais eficiente, mesmo que a diferença provavelmente não seja grande. 1. A complexidade do filteré sempre O(n)enquanto no findFirstMatchingé apenas no pior cenário (quando o elemento que você está procurando é o último ou não no array). 2. filtercria uma matriz completamente nova de elementos filtrados, enquanto o findFirstMatchingjust retorna o elemento solicitado.
0101
No Swift 3, estou recebendo os erros Herança do tipo 'Bool' sem protocolo e sem classe e Uso do tipo não declarado 'T' para este método de extensão.
Isuru
@ Isuru: Essa resposta era bastante antiga e se referia a uma versão antiga do Swift. No Swift 3, você não precisa mais de um método de extensão personalizado para esse fim. Atualizei a resposta de acordo.
Martin R
20

Swift 3.0

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

Swift 2.1

A filtragem nas propriedades do objeto agora é suportada no swift 2.1. Você pode filtrar sua matriz com base em qualquer valor da estrutura ou classe. Aqui está um exemplo

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
fonte
9

Swift 4 ,

Outra maneira de conseguir isso usando a função de filtro,

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

Swift 3

você pode usar o índice (onde :) no Swift 3

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

exemplo

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}
Deokhyun Ko
fonte
Existe um método no swift 3 em que você pode encontrar os índices de uma sub-lista de itens da matriz que atendem à condição $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
fonte
2

Swift 2 ou posterior

Você pode combinar indexOfe mapescrever uma função "encontrar elemento" em uma única linha.

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

Usar filter+ firstparece mais limpo, mas filteravalia todos os elementos na matriz. indexOf+ mapparece complicado, mas a avaliação para quando a primeira correspondência na matriz é encontrada. Ambas as abordagens têm prós e contras.

Yoichi Tagaya
fonte
1

Use contains:

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

Ou você pode tentar o que Martin apontou nos comentários e fazer filteroutra tentativa: Localizar objeto com propriedade na matriz .

cristão
fonte
Isso retornará apenas um booleano? Eu preciso pegar o objeto também, não apenas verificar se está na matriz.
Sahat Yalkabov
Este pressuposto itemé do mesmo tipo que o item na matriz. No entanto, tudo o que tenho é apenas um título view.annotation.title. Eu preciso comparar itens na matriz por este título.
Sahat Yalkabov
Algo como if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov
Martin mostrou outra maneira nos comentários. Verifique o link que ele forneceu.
Christian
1

Outra maneira de obter acesso ao array.index (de: Qualquer) é declarando seu objeto

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

Use Dollar, que é Lo-Dash ou Underscore.js para Swift:

import Dollar

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

Swift 3:

Você pode usar a funcionalidade incorporada do Swifts para encontrar objetos personalizados em uma matriz.

Primeiro, você deve garantir que seu objeto personalizado esteja em conformidade com o: Protocolo equável .

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

Com a funcionalidade Equatable adicionada ao seu objeto, o Swift agora mostrará propriedades adicionais que você pode usar em uma matriz:

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

Para o Swift 3,

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

Por exemplo, se tivéssemos uma matriz de números:

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

Poderíamos encontrar o primeiro número ímpar como este:

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

Isso retornará 4 como um número inteiro opcional, porque o primeiro número ímpar (9) está no índice quatro.

Amul4608
fonte