Obtener una matriz de valores de propiedad de una matriz de objetos

113

Hay una clase llamada Employee.

class Employee {

    var id: Int
    var firstName: String
    var lastName: String
    var dateOfBirth: NSDate?

    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }
}

Y tengo una variedad de Employeeobjetos. Lo que ahora necesito es extraer los ids de todos esos objetos en esa matriz en una nueva matriz.

También encontré esta pregunta similar . Pero está en Objective-C, por lo que se usa valueForKeyPathpara lograr esto.

¿Cómo puedo hacer esto en Swift?

Isuru
fuente

Respuestas:

234

Puede usar el mapmétodo, que transforma una matriz de un cierto tipo en una matriz de otro tipo, en su caso, de una matriz Employeea una matriz de Int:

var array = [Employee]()
array.append(Employee(id: 4, firstName: "", lastName: ""))
array.append(Employee(id: 2, firstName: "", lastName: ""))

let ids = array.map { $0.id }
Antonio
fuente
1
Eso es lo que maphace: transforma una matriz de Employeeen una matriz de Int, llena con el idcampo. Lo que equivale a decir "extraer el campo id de todas las instancias de Employeey ponerlas en una matriz"
Antonio
4
@Isuru, esta respuesta hace exactamente lo que quieres. Crea una nueva matriz llamada idsde todos los idvalores de la matriz de Employees. Tenga en cuenta que deja intacta la matriz original.
vacawama
2
Parece que en Swift 2 beta la sintaxis correcta seríaarray.map( { $0.id })
TotoroTotoro
10
si está usando un opcional, ¡asegúrese de hacerlo! eso. Me tomó horas.
Chris
2
El desenvolvimiento forzado de @Chris suele ser una mala práctica, porque si es nulo, la aplicación se bloqueará. Úselo solo cuando sea estrictamente necesario y prefiera la encuadernación opcional (o cualquier otro desenvolvimiento "suave") en su lugar
Antonio
81

Swift 5 ofrece muchas formas de obtener una matriz de valores de propiedad de una matriz de objetos similares. Según sus necesidades, puede elegir uno de los siguientes seis ejemplos de código de Playground para resolver su problema.


1. mapMétodo de uso

Con Swift, los tipos que se ajustan al Sequenceprotocolo tienen un map(_:)método. El siguiente código de muestra muestra cómo usarlo:

class Employee {
    
    let id: Int, firstName: String, lastName: String
    
    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }

}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]

let idArray = employeeArray.map({ (employee: Employee) -> Int in
    employee.id
})
// let idArray = employeeArray.map { $0.id } // also works
print(idArray) // prints [1, 2, 4]

2. Usando forbucle

class Employee {
    
    let id: Int, firstName: String, lastName: String

    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }

}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]

var idArray = [Int]()    
for employee in employeeArray {
    idArray.append(employee.id)
}
print(idArray) // prints [1, 2, 4]

3. Usando whilebucle

Tenga en cuenta que con Swift, entre bastidores, un forbucle es solo un whilebucle sobre sequenceel iterador de a (consulte IteratorProtocol para obtener más detalles).

class Employee {
    
    let id: Int, firstName: String, lastName: String
    
    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }

}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]

var idArray = [Int]()
var iterator = employeeArray.makeIterator()    
while let employee = iterator.next() {
    idArray.append(employee.id)
}
print(idArray) // prints [1, 2, 4]

4. Uso de una structque se ajusta a IteratorProtocoly Sequenceprotocolos

class Employee {
    
    let id: Int, firstName: String, lastName: String
    
    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }
    
}

struct EmployeeSequence: Sequence, IteratorProtocol {
    
    let employeeArray: [Employee]
    private var index = 0
    
    init(employeeArray: [Employee]) {
        self.employeeArray = employeeArray
    }
    
    mutating func next() -> Int? {
        guard index < employeeArray.count else { return nil }
        defer { index += 1 }
        return employeeArray[index].id
    }
    
}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]
let employeeSequence = EmployeeSequence(employeeArray: employeeArray)
let idArray = Array(employeeSequence)
print(idArray) // prints [1, 2, 4]

5. Usando la Collectionextensión de protocolo yAnyIterator

class Employee {
    
    let id: Int, firstName: String, lastName: String
    
    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }

}

extension Collection where Iterator.Element: Employee {
    
    func getIDs() -> Array<Int> {
        var index = startIndex
        let iterator: AnyIterator<Int> = AnyIterator {
            defer { index = self.index(index, offsetBy: 1) }
            return index != self.endIndex ? self[index].id : nil
        }
        return Array(iterator)
    }
    
}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]

let idArray = employeeArray.getIDs()
print(idArray) // prints [1, 2, 4]

6. Usando NSArrayel value(forKeyPath:)método de KVC y

Tenga en cuenta que este ejemplo requiere class Employeeheredar de NSObject.

import Foundation

class Employee: NSObject {

    @objc let id: Int, firstName: String, lastName: String

    init(id: Int, firstName: String, lastName: String) {
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
    }

}

let employeeArray = [
    Employee(id: 1, firstName: "Jon", lastName: "Skeet"),
    Employee(id: 2, firstName: "Darin", lastName: "Dimitrov"),
    Employee(id: 4, firstName: "Hans", lastName: "Passant")
]

let employeeNSArray = employeeArray as NSArray
if let idArray = employeeNSArray.value(forKeyPath: #keyPath(Employee.id)) as? [Int] {
    print(idArray) // prints [1, 2, 4]
}
Imanou Petit
fuente
5
enorme ... Hasta donde yo entiendo la lista completa de enfoques posibles
Injectios