객체 배열을 Swift의 Dictionary에 매핑
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
}
기능적 접근 방식 map
, flatMap
... 또는 다른 현대적인 Swift 스타일 을 사용하는 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"]
In 버전을 Swift 4
사용하면 @ Tj3n의 접근 방식을 더 깨끗하고 효율적으로 할 수 있습니다 . 임시 사전과 반환 값을 제거하여 더 빠르고 쉽게 읽을 수 있습니다.into
reduce
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)]
let myDict = myArray.reduce(into: [Int: String]()) {
$0[$1.position] = $1.name
}
print(myDict)
참고 : Swift 3에서는 작동하지 않습니다.
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)})
축소 기능을 사용할 수 있습니다. 먼저 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
}
아마 이런 건가요?
myArray.forEach({ myDictionary[$0.position] = $0.name })
이것은 내가 사용하고있는 것입니다
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}
그 때문에 지난 2 개 라인을 결합 할 수있는 방법이 있다면 좋을 것이다 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})
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
}
}
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"]
참고 URL : https://stackoverflow.com/questions/38454952/map-array-of-objects-to-dictionary-in-swift
'development' 카테고리의 다른 글
Android 용 React Native에서 그림자를 설정하는 방법은 무엇입니까? (0) | 2020.12.13 |
---|---|
탭 바에서 비활성 아이콘 / 텍스트 색상을 변경하는 방법은 무엇입니까? (0) | 2020.12.13 |
바이트 배열의 데이터를 문자로 인쇄하는 방법 (0) | 2020.12.13 |
XAMPP-예기치 않게 MySQL 종료 (0) | 2020.12.13 |
iOS에서 애니메이션으로 탭 막대를 숨기는 방법은 무엇입니까? (0) | 2020.12.13 |