[swift] Swift의 사전에 map ()을 적용하는 가장 깨끗한 방법은 무엇입니까?

사전의 모든 키에 함수를 매핑하고 싶습니다. 다음과 같은 것이 효과가 있기를 희망했지만 필터를 사전에 직접 적용 할 수는 없습니다. 이것을 달성하는 가장 깨끗한 방법은 무엇입니까?

이 예제에서는 각 값을 1 씩 늘리려 고합니다. 그러나이 예제에서는 부수적입니다. 주 목적은 map ()을 사전에 적용하는 방법을 알아내는 것입니다.

var d = ["foo" : 1, "bar" : 2]

d.map() {
    $0.1 += 1
}



답변

스위프트 4+

좋은 소식! Swift 4에는 mapValues(_:)동일한 키는 있지만 값이 다른 사전의 사본을 구성 하는 방법이 포함되어 있습니다. 그것은 또한 포함 filter(_:)반환 과부하 Dictionaryinit(uniqueKeysWithValues:)init(_:uniquingKeysWith:)을 만들 초기화를 Dictionary튜플 임의의 순서로한다. 즉, 키와 값을 모두 변경하려면 다음과 같이 말할 수 있습니다.

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })

누락 된 요소에 대한 기본값을 대체하고, 값을 그룹화 (컬렉션을 배열의 사전으로 변환, 일부 함수를 통해 콜렉션을 맵핑 한 결과로 키가 지정된) 등으로 사전을 병합하는 새로운 API도 있습니다.

이러한 기능을 소개 한 제안서 SE-0165에 대해 토론 하면서이 스택 오버플로 답변을 여러 번 제기했으며, 많은 수의 업 보트가 수요를 입증하는 데 도움이되었다고 생각합니다. Swift를 개선하는 데 도움을 주셔서 감사합니다.


답변

Swift 5에서는 다음 5 가지 스 니펫 중 하나를 사용 하여 문제를 해결할 수 있습니다.


#1. Dictionary mapValues(_:)방법을 사용하여

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.mapValues { value in
    return value + 1
}
//let newDictionary = dictionary.mapValues { $0 + 1 } // also works

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 2. 사용 Dictionary map방법 및 init(uniqueKeysWithValues:)이니셜 라이저

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let tupleArray = dictionary.map { (key: String, value: Int) in
    return (key, value + 1)
}
//let tupleArray = dictionary.map { ($0, $1 + 1) } // also works

let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray)

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

#삼. 사용 Dictionary reduce(_:_:)방법 또는 reduce(into:_:)방법을

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.reduce([:]) { (partialResult: [String: Int], tuple: (key: String, value: Int)) in
    var result = partialResult
    result[tuple.key] = tuple.value + 1
    return result
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
let dictionary = ["foo": 1, "bar": 2, "baz": 5]

let newDictionary = dictionary.reduce(into: [:]) { (result: inout [String: Int], tuple: (key: String, value: Int)) in
    result[tuple.key] = tuple.value + 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 4. Dictionary subscript(_:default:)아래 첨자 사용

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

var newDictionary = [String: Int]()
for (key, value) in dictionary {
    newDictionary[key, default: value] += 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 5. Dictionary subscript(_:)아래 첨자 사용

let dictionary = ["foo": 1, "bar": 2, "baz": 5]

var newDictionary = [String: Int]()
for (key, value) in dictionary {
    newDictionary[key] = value + 1
}

print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]


답변

여기에있는 대부분의 답변은 전체 사전 (키 값) 을 매핑하는 방법에 중점을 두지 만 질문은 실제로 값만 매핑하려고했습니다. 키와 값을 모두 맵핑하면 키가 중복 될 수 있지만 값을 맵핑하면 동일한 수의 항목을 보장 할 수 있으므로 이는 중요한 차이점입니다.

여기 mapValues에 값만 매핑 할 수 있는 확장 이 있습니다. 또한 init일련의 키 / 값 쌍을 사용하여 사전을 확장합니다 . 이는 배열에서 사전을 초기화하는 것보다 약간 더 일반적입니다.

extension Dictionary {
    init<S: SequenceType where S.Generator.Element == Element>
      (_ seq: S) {
        self.init()
        for (k,v) in seq {
            self[k] = v
        }
    }

    func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> {
        return Dictionary<Key,T>(zip(self.keys, self.values.map(transform)))
    }

}


답변

가장 깨끗한 방법은 map사전에 추가 하는 것입니다.

extension Dictionary {
    mutating func map(transform: (key:KeyType, value:ValueType) -> (newValue:ValueType)) {
        for key in self.keys {
            var newValue = transform(key: key, value: self[key]!)
            self.updateValue(newValue, forKey: key)
        }
    }
}

작동하는지 확인 :

var dic = ["a": 50, "b": 60, "c": 70]

dic.map { $0.1 + 1 }

println(dic)

dic.map { (key, value) in
    if key == "a" {
        return value
    } else {
        return value * 2
    }
}

println(dic)

산출:

[c: 71, a: 51, b: 61]
[c: 142, a: 51, b: 122]


답변

reduce지도 대신 사용할 수도 있습니다 . 더 많은 reduce일을 map할 수 있습니다!

let oldDict = ["old1": 1, "old2":2]

let newDict = reduce(oldDict, [String:Int]()) { dict, pair in
    var d = dict
    d["new\(pair.1)"] = pair.1
    return d
}

println(newDict)   //  ["new1": 1, "new2": 2]

이것을 확장으로 감싸는 것은 상당히 쉽지만 확장명이 없어도 한 번의 함수 호출로 원하는 것을 수행 할 수 있습니다.


답변

당신이 할 수있는 것으로 나타났습니다. 당신이해야 할 일은 MapCollectionView<Dictionary<KeyType, ValueType>, KeyType>사전 keys메소드 에서 반환 된 배열을 만드는 것입니다. ( 정보는 여기에 ) 그런 다음이 배열을 매핑 할 수 있습니다, 그리고 다시 사전에 업데이트 된 값을 전달합니다.

var dictionary = ["foo" : 1, "bar" : 2]

Array(dictionary.keys).map() {
    dictionary.updateValue(dictionary[$0]! + 1, forKey: $0)
}

dictionary


답변

Swift Standard Library Reference에 따르면 map은 배열의 함수입니다. 사전이 아닙니다.

그러나 사전을 반복하여 키를 수정할 수 있습니다.

var d = ["foo" : 1, "bar" : 2]

for (name, key) in d {
    d[name] = d[name]! + 1
}