Получить массив значений свойств из массива объектов

113

Есть класс под названием 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
    }
}

И у меня есть массив Employeeобъектов. Теперь мне нужно извлечь ids всех этих объектов в этом массиве в новый массив.

Я тоже нашел этот похожий вопрос . Но он находится в Objective-C, поэтому используется valueForKeyPathдля этого.

Как я могу это сделать в Swift?

Исуру
источник

Ответы:

235

Вы можете использовать mapметод, который преобразует массив определенного типа в массив другого типа - в вашем случае из массива Employeeв массив Int:

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

let ids = array.map { $0.id }
Антонио
источник
1
Вот что mapделает - он преобразует массив Employeeв массив Int, заполненный idполем. Это эквивалентно высказыванию «извлеките поле id из всех экземпляров Employeeи поместите их в массив»
Антонио
4
@Isuru, этот ответ делает именно то, что вы хотите. Он создает новый массив, вызывающий idsвсе idзначения из массива Employees. Обратите внимание: он оставляет без изменений исходный массив.
vacawama
2
Похоже, что в бета-версии Swift 2 правильный синтаксис будетarray.map( { $0.id })
TotoroTotoro
10
если вы используете необязательный, убедитесь, что вы! Это. У меня ушло несколько часов.
Крис
2
Принудительное развертывание @Chris обычно является плохой практикой, потому что, если оно равно нулю, это приведет к сбою приложения. Используйте его только тогда, когда это строго необходимо, и вместо этого предпочтите дополнительное связывание (или любое другое «мягкое» развертывание)
Антонио
81

Swift 5 предлагает множество способов получить массив значений свойств из массива похожих объектов. В соответствии с вашими потребностями вы можете выбрать один из шести следующих примеров кода Playground для решения вашей проблемы.


1. Использование mapметода

В Swift типы, соответствующие Sequenceпротоколу, имеют map(_:)метод. В следующем примере кода показано, как его использовать:

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. Использование forцикла

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. Использование whileцикла

Обратите внимание, что в Swift за кулисами forцикл - это просто whileцикл над sequenceитератором (подробнее см. IteratorProtocol ).

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. Использование , structкоторое соответствует IteratorProtocolи Sequenceпротоколов

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. Использование Collectionрасширения протокола иAnyIterator

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. Использование КВЦ и NSArray«S value(forKeyPath:)метод

Обратите внимание, что в этом примере требуется class Employeeнаследование от 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]
}
Иману Пети
источник
5
огромный ... Насколько я понимаю, полный список возможных подходов
Injectios