Person
의 개체 배열이 있습니다.
class Person {
let name:String
let position:Int
}
배열은 다음과 같습니다.
let myArray = [p1,p1,p3]
클래식 솔루션 myArray
의 사전 이되도록 매핑하고 싶습니다 [position:name]
.
var myDictionary = [Int:String]()
for person in myArray {
myDictionary[person.position] = person.name
}
Swift가 기능적 접근 방식 map
, flatMap
… 또는 다른 현대적인 Swift 스타일을 사용 하는 우아한 방법이 있습니까?
답변
Okay map
는 이것의 좋은 예가 아닙니다. 루핑과 똑같기 때문에 reduce
대신 사용할 수 있습니다. 각 객체를 결합하여 단일 값으로 변환해야합니다.
let myDictionary = myArray.reduce([Int: String]()) { (dict, person) -> [Int: String] in
var dict = dict
dict[person.position] = person.name
return dict
}
//[2: "b", 3: "c", 1: "a"]
Swift 4 이상에서는 더 명확한 구문을 위해 아래 답변을 사용하십시오.
답변
Swift 4
@ Tj3n의 접근 방식 into
은 reduce
It 버전을 사용하여보다 깨끗하고 효율적으로 할 수 있기 때문에 임시 사전과 반환 값을 제거하여 더 빠르고 쉽게 읽을 수 있습니다.
샘플 코드 설정 :
struct Person {
let name: String
let position: Int
}
let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]
Into
매개 변수에 결과 유형의 빈 사전이 전달됩니다.
let myDict = myArray.reduce(into: [Int: String]()) {
$0[$1.position] = $1.name
}
전달 된 유형의 사전을 직접 반환합니다 into
.
print(myDict) // [2: "c", 0: "h", 4: "b"]
답변
Swift 4 부터는 이것을 매우 쉽게 할 수 있습니다. 있다 이 개 새로운 튜플의 순서에서 (키와 값의 쌍을) 사전을 구축 이니셜. 키가 고유하다고 보장되는 경우 다음을 수행 할 수 있습니다.
let persons = [Person(name: "Franz", position: 1),
Person(name: "Heinz", position: 2),
Person(name: "Hans", position: 3)]
Dictionary(uniqueKeysWithValues: persons.map { ($0.position, $0.name) })
=> [1: "Franz", 2: "Heinz", 3: "Hans"]
키가 중복되면 런타임 오류와 함께 실패합니다. 이 경우이 버전을 사용할 수 있습니다.
let persons = [Person(name: "Franz", position: 1),
Person(name: "Heinz", position: 2),
Person(name: "Hans", position: 1)]
Dictionary(persons.map { ($0.position, $0.name) }) { _, last in last }
=> [1: "Hans", 2: "Heinz"]
이것은 for 루프로 작동합니다. 값을 “덮어 쓰지”않고 첫 번째 매핑을 고수하지 않으려면 다음을 사용할 수 있습니다.
Dictionary(persons.map { ($0.position, $0.name) }) { first, _ in first }
=> [1: "Franz", 2: "Heinz"]
Swift 4.2 는 시퀀스 요소를 사전으로 그룹화 하는 세 번째 이니셜 라이저를 추가합니다 . 사전 키는 클로저에 의해 파생됩니다. 동일한 키를 가진 요소는 시퀀스와 동일한 순서로 배열에 배치됩니다. 이를 통해 위와 유사한 결과를 얻을 수 있습니다. 예를 들면 :
Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.last! }
=> [1: Person(name: "Hans", position: 1), 2: Person(name: "Heinz", position: 2)]
Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.first! }
=> [1: Person(name: "Franz", position: 1), 2: Person(name: "Heinz", position: 2)]
답변
사용자 정의 이니셜 라이저를 작성할 수 있습니다. Dictionary
예를 들어 튜플에서 유형에 대한 .
extension Dictionary {
public init(keyValuePairs: [(Key, Value)]) {
self.init()
for pair in keyValuePairs {
self[pair.0] = pair.1
}
}
}
다음 map
배열에 사용 하십시오 Person
.
var myDictionary = Dictionary(keyValuePairs: myArray.map{($0.position, $0.name)})
답변
KeyPath 기반 솔루션은 어떻습니까?
extension Array {
func dictionary<Key, Value>(withKey key: KeyPath<Element, Key>, value: KeyPath<Element, Value>) -> [Key: Value] {
return reduce(into: [:]) { dictionary, element in
let key = element[keyPath: key]
let value = element[keyPath: value]
dictionary[key] = value
}
}
}
사용 방법은 다음과 같습니다.
struct HTTPHeader {
let field: String, value: String
}
let headers = [
HTTPHeader(field: "Accept", value: "application/json"),
HTTPHeader(field: "User-Agent", value: "Safari"),
]
let allHTTPHeaderFields = headers.dictionary(withKey: \.field, value: \.value)
// allHTTPHeaderFields == ["Accept": "application/json", "User-Agent": "Safari"]
답변
축소 기능을 사용할 수 있습니다. 먼저 Person 클래스에 대해 지정된 이니셜 라이저를 만들었습니다.
class Person {
var name:String
var position:Int
init(_ n: String,_ p: Int) {
name = n
position = p
}
}
나중에 값 배열을 초기화했습니다.
let myArray = [Person("Bill",1),
Person("Steve", 2),
Person("Woz", 3)]
마지막으로 사전 변수의 결과는 다음과 같습니다.
let dictionary = myArray.reduce([Int: Person]()){
(total, person) in
var totalMutable = total
totalMutable.updateValue(person, forKey: total.count)
return totalMutable
}
답변
이것은 내가 사용하고있는 것입니다
struct Person {
let name:String
let position:Int
}
let persons = [Person(name: "Franz", position: 1),
Person(name: "Heinz", position: 2),
Person(name: "Hans", position: 3)]
var peopleByPosition = [Int: Person]()
persons.forEach{peopleByPosition[$0.position] = $0}
마지막 두 줄을 결합하는 방법이 있다면 좋을 peopleByPosition
것입니다.let
.
그렇게하는 Array를 확장 할 수 있습니다!
extension Array {
func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable {
var map = [T: Element]()
self.forEach{ map[block($0)] = $0 }
return map
}
}
그럼 우리는 할 수 있습니다
let peopleByPosition = persons.mapToDict(by: {$0.position})