codememo

Swift의 사전에 객체 배열 매핑

tipmemo 2023. 4. 28. 20:37
반응형

Swift의 사전에 객체 배열 매핑

나는 여러 가지를 가지고 있습니다.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 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)]

Into매개 변수가 결과 유형의 빈 사전을 전달합니다.

let myDict = myArray.reduce(into: [Int: String]()) {
    $0[$1.position] = $1.name
}

전달된 형식의 사전을 직접 반환합니다.into:

print(myDict) // [2: "c", 0: "h", 4: "b"]

알았어요.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 이상에서는 더 명확한 구문을 위해 아래 답변을 사용하십시오.

스위프트 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)]

KeyPath 기반 솔루션은 어떻습니까?

extension Array {
  func dictionary<Key, Value>(withKey key: KeyPath<Element, Key>, value: KeyPath<Element, Value>) -> [Key: Value] {
    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")
]

headers.dictionary(withKey: \.field, value: \.value) // ["Accept": "application/json", "User-Agent": "Safari"]

다음에 대한 사용자 정의 이니셜라이저를 작성할 수 있습니다.Dictionarytype(예: 튜플에서):

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

이것이 제가 사용해온 것입니다.

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.

어레이를 확장할 수 있습니다!

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

축소 함수를 사용할 수 있습니다.먼저 사용자 클래스에 지정된 이니셜라이저를 만들었습니다.

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 })
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
    }
}
extension Array {

    func toDictionary() -> [Int: Element] {
        self.enumerated().reduce(into: [Int: Element]()) { $0[$1.offset] = $1.element }
    }
    
}

언급URL : https://stackoverflow.com/questions/38454952/map-array-of-objects-to-dictionary-in-swift

반응형