¿Encontrar un objeto en la matriz?


144

¿Swift tiene algo como _.findWhere en Underscore.js?

Tengo una matriz de estructuras de tipo Ty me gustaría verificar si la matriz contiene un objeto de estructura cuya namepropiedad es igual a Foo.

Intenté usar find()y filter()pero solo funcionan con tipos primitivos, por ejemplo, Stringo Int. Lanza un error sobre no cumplir con el Equitableprotocolo o algo así.


Esto podría ser lo que está buscando: Buscar objeto con propiedad en matriz .
Martin R

por qué no convertir a nsdictionary y buscar
arco largo

3
Creo que find ya no está disponible en Swift 2.0 ... Estaba convirtiendo un código 1.2 a Swift 2.0 y en su lugar decía que usaba IndexOf.
Swift Soda

Respuestas:


91

FWIW, si no desea utilizar una función o extensión personalizada, puede:

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

Esto genera la namematriz primero, luegofind de ella.

Si tiene una gran variedad, es posible que desee hacer:

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

o tal vez:

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

Esto es aun mejor. Marcaré esto como respuesta ya que en general parece más simple y no requiere crear una función personalizada.
Sahat Yalkabov

27
A partir de Swift 2.0 puede usar: array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
A partir de Swift 3.0 puede usar: array.first (donde: {$ 0.name == "Foo"}) si necesita el objeto
Brett

¿Cómo puedo verificar que $ 0.name contiene la cadena "foo"? las respuestas son sobre coincidencia exacta. Necesito contiene cadena. ¿Alguien puede dar la sintaxis para eso
Azik Abdullah

290

SWIFT 5

Verificar si el elemento existe

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

Obtén el elemento

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

Obtenga el elemento y su desplazamiento

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
}

Consigue el desplazamiento

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

66
Buena respuesta de estilo rápido!
Zoltán

44
Gracias, eres mi ahorrador, esto ha limpiado mucho mi código
AD Progress

cómo verificar múltiples condiciones, digamos que necesito verificar si la matriz realiza $0.name == "foo"una operación y $0.name == "boo" otra operación
Midhun Narayan

Esto me ayudó más que la respuesta aceptada en 2020.
Psiloc

¿Cómo puedo verificar que $ 0.name contiene la cadena "foo"? ¿Alguien puede dar la sintaxis para eso
Azik Abdullah

131

Puede usar el indexmétodo disponible Arraycon un predicado ( consulte la documentación de Apple aquí ).

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

Para su ejemplo específico, esto sería:

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í, esto funciona solo con swift 2.0. Lo siento debería haberlo mencionado.
user3799504

@ user3799504 significado de $ 0?
Pramod Tapaniya

$ 0 es una abreviatura para el primer argumento del cierre. En este caso se refiere a self.generator.element o cada elemento de la matriz. Consulte: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
user3799504

1
¿Qué tal Swift 3?
adnako

38

Swift 3

Si necesita el objeto, use:

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

(Si tiene más de un objeto llamado "Foo" first, devolverá el primer objeto de un pedido no especificado)


Gracias por esto. ¡Esto debería estar allá arriba!
NerdyTherapist

3
Esto es bueno, gracias! También se puede escribir así:array.first {$0.name == "Foo"}
Roland T.

2
En Swift3 debe serarray.first(where: {$0.name == "Foo"})
Daniel

Con la nota de Daniel, esta es LA mejor respuesta correcta para Swift 3. No use el mapa, el filtro para este propósito; iteran sobre colecciones enteras, lo que puede ser un gran desperdicio.
Womble

20

Puede filtrar la matriz y luego simplemente elegir el primer elemento, como se muestra en Buscar objeto con propiedad en matriz .

O define una extensión 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
    }
}

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

En Swift 3 puede usar el first(where:)método existente (como se menciona en un comentario ):

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

En términos de eficiencia, ¿cómo se compara esto con array.lazy.filter( predicate ).first? ¿Qué tan eficiente es .lazy para arreglos pequeños?
Pat Niemeyer

@ PatNiemeyer: No sé, tendrías que medir el rendimiento y comparar.
Martin R

@PatNiemeyer la solución anterior seguramente será más eficiente, aunque la diferencia probablemente no sea grande. 1. La complejidad de la filteres siempre, O(n)mientras que en la findFirstMatchinges solo en el peor escenario (cuando el elemento que está buscando es el último o no está en la matriz). 2. filtercrea una matriz completamente nueva de elementos filtrados mientras que el findFirstMatchingsolo devuelve el elemento solicitado.
0101

En Swift 3, obtengo la herencia de errores de tipo 'protocolo' y 'clase' sin protocolo y uso de tipo 'T' no declarado para este método de extensión.
Isuru

@Isuru: Esa respuesta era bastante antigua y se refería a una versión antigua de Swift. En Swift 3 ya no necesita un método de extensión personalizado para ese fin, he actualizado la respuesta en consecuencia.
Martin R

20

Swift 3.0

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

Swift 2.1

El filtrado en las propiedades del objeto ahora es compatible con swift 2.1. Puede filtrar su matriz en función de cualquier valor de la estructura o clase aquí es un ejemplo

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 ,

Otra forma de lograr esto usando la función de filtro,

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

8

Swift 3

puedes usar index (where :) en Swift 3

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

ejemplo

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

¿Hay algún método en Swift 3 donde pueda encontrar los índices de una sublista de elementos de la matriz que cumplan la condición $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 posterior

Puede combinar indexOfy mapescribir una función "buscar elemento" en una sola línea.

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+ se firstve más limpio, pero filterevalúa todos los elementos de la matriz. indexOf+ mapparece complicado, pero la evaluación se detiene cuando se encuentra la primera coincidencia en la matriz. Ambos enfoques tienen pros y contras.


1

Uso contains:

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

O puede probar lo que Martin le indicó, en los comentarios y darle filterotra oportunidad: Buscar objeto con propiedad en matriz .


¿Eso solo devolverá un valor booleano? También necesito obtener el objeto, no solo verificar si está en la matriz.
Sahat Yalkabov

Esta suposición itemes del mismo tipo que el elemento en la matriz. Sin embargo, todo lo que tengo es solo un título view.annotation.title. Necesito comparar elementos en la matriz por este título.
Sahat Yalkabov

Algo así como if contains(yourArray, view.annotation.title) { // code goes here }.
Sahat Yalkabov

Martin te mostró otra forma en los comentarios. Verifique el enlace que proporcionó.
Christian

1

Otra forma de obtener acceso a array.index (de: Any) es declarando su objeto

import Foundation
class Model: NSObject {  }

1

Use Dollar que es Lo-Dash o Underscore.js para Swift:

import Dollar

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

1

Swift 3:

Puede usar la funcionalidad integrada de Swifts para buscar objetos personalizados en una matriz.

Primero debe asegurarse de que su objeto personalizado se ajuste al protocolo : 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 funcionalidad Equatable agregada a su objeto, Swift ahora le mostrará propiedades adicionales que puede usar en una 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

1

Para Swift 3,

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

0

Por ejemplo, si tuviéramos una serie de números:

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

Podríamos encontrar el primer número impar como este:

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

Eso devolverá 4 como un entero opcional, porque el primer número impar (9) está en el índice cuatro.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.