다른 사전에 항목 사전을 추가하는 방법


172

Swift의 배열은 + = 연산자를 지원하여 한 배열의 내용을 다른 배열에 추가합니다. 사전을 위해 쉬운 방법이 있습니까?

예 :

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var combinedDict = ... (some way of combining dict1 & dict2 without looping)


fromDict.forEach {intoDict[$0] = $1}
Sazzad Hissain Khan

답변:


171

+=대한 연산자를 정의 할 수 있습니다 ( Dictionary예 :

func += <K, V> (left: inout [K:V], right: [K:V]) { 
    for (k, v) in right { 
        left[k] = v
    } 
}

1
오, 나는 이것에 대한 적절한 일반적인 선언을 찾기 위해 너무 고투했다. 나는 이것을 제외한 모든 것을 시도했다. 하지만 당신은을 놓을 수 @assignmentreturn이미 왼쪽 돌연변이하고 있습니다. 편집 : 실제로 오류가 없어도 @assignment그대로 있어야 한다고 생각 합니다.
Roland

14
더 많은 문법 설탕 : func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
Ivan Vavilov

48
@animal_chin 언어 자체의 절반을 구현해야합니까? 예. 깊은 인상. 오해하지 마십시오. 연산자 과부하를 좋아합니다. 내장 기능을 기본 기능으로 사용하는 것을 좋아하지 않습니다.
devios1

2
@devios 하하 다음 스위프트의 REPO에 풀 요청합니다 D를 분명히 애플이 arsed 할 수 없기 때문에
CommaToast

6
SwifterSwift 라이브러리 에서 바로 가져 오기 :public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }
Justin Oroz

99

Swift 4에서는 다음을 사용해야합니다 merging(_:uniquingKeysWith:).

예:

let dictA = ["x" : 1, "y": 2, "z": 3]
let dictB = ["x" : 11, "y": 22, "w": 0]

let resultA = dictA.merging(dictB, uniquingKeysWith: { (first, _) in first })
let resultB = dictA.merging(dictB, uniquingKeysWith: { (_, last) in last })

print(resultA) // ["x": 1, "y": 2, "z": 3, "w": 0]
print(resultB) // ["x": 11, "y": 22, "z": 3, "w": 0]

1
// mutable : var dictA = [ "x": 1, "y": 2, "z": 3] var dictB = [ "x": 11, "y": 22, "w": 0] dictA. merge (dictB, uniquingKeysWith : {(첫번째, _) 첫 번째}) print (dictA) // [ "x": 1, "y": 2, "z": 3, "w": 0]
muthukumar

1
이 답변에 표시된 두 번째 예는와 같습니다 [NSMutableDictionary addEntriesFromDictionary:].
orj

92

어때요?

dict2.forEach { (k,v) in dict1[k] = v }

그러면 dict2의 모든 키와 값이 dict1에 추가됩니다.


43
좋은 해결책. 약간 더 짧은 : dict2.forEach {dict1 [$ 0] = $ 1}
Brett

1
이것은 훌륭한 솔루션이지만 Swift 4의 경우 Closure tuple parameter '(key: _, value: _)' does not support destructuring(적어도이 글을 쓰는 시점 에는) 오류가 발생했을 가능성이 큽니다 . [이 stackoverflow 답변] ( stackoverflow.com/questions/44945967/… ) 에 따라 클로저를 재구성해야합니다 .
JonnyB

78

현재 Swift Standard Library Reference for Dictionary를 보면 사전을 다른 사전으로 쉽게 업데이트 할 수있는 방법이 없습니다.

당신은 그것을 할 확장을 쓸 수 있습니다

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

extension Dictionary {
    mutating func update(other:Dictionary) {
        for (key,value) in other {
            self.updateValue(value, forKey:key)
        }
    }
}

dict1.update(dict2)
// dict1 is now ["a" : "foo", "b" : "bar]

3
이것은 사전을 확장하는 데 유용합니다!
Marc Attinasi

76

Swift 4merging(_:uniquingKeysWith:)귀하의 경우에 다음을 제공합니다 .

let combinedDict = dict1.merging(dict2) { $1 }

속기 폐쇄는을 반환 $1하므로 dict2의 값은 키와 충돌 할 때 사용됩니다.


1
이것이 Apple 문서에서 언급 한 것 중 가장 간결하고 가장 근접한 것임을 지적하고 싶었습니다 - (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;. 중복으로 수행 할 작업과 관련하여 "두 사전에 동일한 키가 포함되어 있으면 해당 키에 대한 수신 사전의 이전 값 개체에 릴리스 메시지가 전송되고 새 값 개체가 대신 사용됩니다." Swift 버전 또는 merge (_ : uniquingKeysWith :)에서 두 번째 값을 반환하는 $1것은 수행하는 것과 동일 addEntriesFromDictionary합니다.
Tim Fuqua

31

Swift 라이브러리에 내장되어 있지 않지만 연산자 오버로드로 원하는 것을 추가 할 수 있습니다. 예 :

func + <K,V>(left: Dictionary<K,V>, right: Dictionary<K,V>) 
    -> Dictionary<K,V> 
{
    var map = Dictionary<K,V>()
    for (k, v) in left {
        map[k] = v
    }
    for (k, v) in right {
        map[k] = v
    }
    return map
}

이렇게하면 +연산자에 사전을 추가하는 데 사용할 수있는 사전에 대한 연산자가 오버로드됩니다. +예 :

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]

1
+ =가 dict을 제자리에 업데이트하도록 할 수도 있습니다 (선택적 질문에 따라).
Rod

3
매개 변수 를 선언 한 다음 값을 복사 map하면 첫 번째 for (k, v)...루프를 제거하고 삭제할 수 있습니다 . leftvarright
네이트 쿡

2
@NateCook은 사전 +연산자를 변경하지 않으므로 사전 연산자는 작동하지 않습니다 .
mythz

고마워 귀하의 답변은 내가 게시 한 샘플 코드에 대해 더 정확했을 수도 있고, 다른 하나는 내 질문에 따라 내가 원했던 것입니다. 내 나쁜, 어쨌든 당신에게
공감대를

2
@mythz +연산자 오버로드가의 메소드가 아니기 때문에 실제로 변경되지 는 않습니다 Dictionary. 간단한 함수입니다. 변수 left매개 변수에 대한 변경 사항 은 함수 외부에서 볼 수 없습니다.
Nate Cook

28

스위프트 3 :

extension Dictionary {

    mutating func merge(with dictionary: Dictionary) {
        dictionary.forEach { updateValue($1, forKey: $0) }
    }

    func merged(with dictionary: Dictionary) -> Dictionary {
        var dict = self
        dict.merge(with: dictionary)
        return dict
    }
}

let a = ["a":"b"]
let b = ["1":"2"]
let c = a.merged(with: b)

print(c) //["a": "b", "1": "2"]

6
약간 더 나은func merged(with dictionary: Dictionary<Key,Value>) -> Dictionary<Key,Value> { var copy = self dictionary.forEach { copy.updateValue($1, forKey: $0) } return copy }
Alexander Vasenin 1

16

스위프트 2.0

extension Dictionary {

    mutating func unionInPlace(dictionary: Dictionary) {
        dictionary.forEach { self.updateValue($1, forKey: $0) }
    }

    func union(var dictionary: Dictionary) -> Dictionary {
        dictionary.unionInPlace(self)
        return dictionary
    }
}

non-
mutating

union함수는에 전달 된 값을 가짐 var으로써 복사 된 사전이 변경 될 수 있음을 의미합니다. func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }한 줄만 있으면 보다 깨끗 합니다.
MaddTheSane

2
var params는 더 이상 사용되지 않으며 Swift 3에서 제거됩니다.이를 수행하는 가장 좋은 방법은 본문에서 var를 선언하는 것 var dictionary = dictionary입니다. 여기에서 : github.com/apple/swift-evolution/blob/master/proposals/…
Daniel Wood

형태 보증 된 일을 더하려면 추가 <Key, Value>에게 Dictionary의.
Raphael

12

불변

불변의 사전을 +연산자 와 결합 / 결합하는 것을 선호 하므로 다음과 같이 구현했습니다.

// Swift 2
func + <K,V> (left: Dictionary<K,V>, right: Dictionary<K,V>?) -> Dictionary<K,V> {
    guard let right = right else { return left }
    return left.reduce(right) {
        var new = $0 as [K:V]
        new.updateValue($1.1, forKey: $1.0)
        return new
    }
}

let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
let attributes: [String:AnyObject] = ["File":"Auth.swift"]

attributes + moreAttributes + nil //["Function": "authenticate", "File": "Auth.swift"]    
attributes + moreAttributes //["Function": "authenticate", "File": "Auth.swift"]
attributes + nil //["File": "Auth.swift"]

변하기 쉬운

// Swift 2
func += <K,V> (inout left: Dictionary<K,V>, right: Dictionary<K,V>?) {
    guard let right = right else { return }
    right.forEach { key, value in
        left.updateValue(value, forKey: key)
    }
}

let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
var attributes: [String:AnyObject] = ["File":"Auth.swift"]

attributes += nil //["File": "Auth.swift"]
attributes += moreAttributes //["File": "Auth.swift", "Function": "authenticate"]

5
왜 이것이 기본적으로 신속하게 내장되어 있지 않은지 이해하지 못합니까?
ioquatix

1
"Immutable"솔루션에서 오른쪽을 재정의하기 위해 왼쪽에서 값을 계획하고 있습니까? 나는 당신이 right.reduce(left)적어도 예상되는 행동 imo (그리고 그것은 두 번째 예의 행동)임을 의미한다고 생각합니다 . ["A":1] + ["A":2]출력["A":2]
ccwasden

출력은 코드에 해당합니다. 초기 값이 지금처럼 오른쪽이 되길 원합니다.
ricardopereira 2016 년

12

사전 확장이 필요 없습니다. Swift (Xcode 9.0+) 사전에는이 기능이 있습니다. 봐 가지고 여기를 . 아래는 사용 방법에 대한 예입니다.

  var oldDictionary = ["a": 1, "b": 2]
  var newDictionary = ["a": 10000, "b": 10000, "c": 4]

  oldDictionary.merge(newDictionary) { (oldValue, newValue) -> Int in
        // This closure return what value to consider if repeated keys are found
        return newValue 
  }
  print(oldDictionary) // Prints ["b": 10000, "a": 10000, "c": 4]

2
위 예제의 기능적 스타일을 추가하고 있습니다.oldDictionary.merge(newDictionary) { $1 }
Andrej

11

확장을 사용하는 더 읽기 쉬운 변형.

extension Dictionary {    
    func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
        var mutableCopy = self        
        for (key, value) in dict {
            // If both dictionaries have a value for same key, the value of the other dictionary is used.           
            mutableCopy[key] = value 
        }        
        return mutableCopy
    }    
}

3
아주 좋고 깨끗한 솔루션!
user3441734

11

당신은 이것을 시도 할 수 있습니다

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)

10

reduce를 사용하여 병합 할 수도 있습니다. 운동장에서 이것을 시도하십시오

let d1 = ["a":"foo","b":"bar"]
let d2 = ["c":"car","d":"door"]

let d3 = d1.reduce(d2) { (var d, p) in
   d[p.0] = p.1
   return d
}

이 흥미로운 보이지만, 무엇 dp?
rob

1
d는 감소 블록의 각 반복의 지속 결과이고 p는 감소되고있는 콜렉션의 요소입니다.
farhadf

1
이것은 스위프트 3.0 베타에서 충돌하는 것으로 보인다
possen

var에 매개 변수가 빠른 3에서 사용되지 않습니다
드미트리 Klochkov을

이것은 여기에 언급 된 것 중 내가 가장 좋아하는 솔루션입니다. 훌륭한 간결한 솔루션을 위해 필터 / 맵 / 감소가 다시 승리합니다.
gokeji

7

SwifterSwift Library를 권장합니다 . 그러나 전체 라이브러리와 모든 추가 기능을 사용하지 않으려는 경우 사전 확장을 사용할 수 있습니다.

스위프트 3+

public extension Dictionary {
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
        rhs.forEach({ lhs[$0] = $1})
    }
}

실제로, SE-110이 복귀되었으므로 Swift 4 버전은 Swift 3 버전과 동일해야합니다.
BallpointBen

5

updateValue (forKey :) 메소드를 통해 병합하려는 값과 키 값 조합을 반복하여 추가 할 수 있습니다.

dictionaryTwo.forEach {
    dictionaryOne.updateValue($1, forKey: $0)
}

이제 dictionaryTwo의 모든 값이 dictionaryOne에 추가되었습니다.


4

@farhadf의 답변과 동일하지만 Swift 3에 채택되었습니다.

let sourceDict1 = [1: "one", 2: "two"]
let sourceDict2 = [3: "three", 4: "four"]

let result = sourceDict1.reduce(sourceDict2) { (partialResult , pair) in
    var partialResult = partialResult //without this line we could not modify the dictionary
    partialResult[pair.0] = pair.1
    return partialResult
}

4

스위프트 3, 사전 확장 :

public extension Dictionary {

    public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
        for (k, v) in rhs {
            lhs[k] = v
        }
    }

}

4

Swift 4의 경우 더욱 간소화 된 과부하가 발생합니다.

extension Dictionary {
    static func += (lhs: inout [Key:Value], rhs: [Key:Value]) {
        lhs.merge(rhs){$1}
    }
    static func + (lhs: [Key:Value], rhs: [Key:Value]) -> [Key:Value] {
        return lhs.merging(rhs){$1}
    }
}

3

다음 과 같이 Dictionary확장명을 추가 할 수 있습니다 .

extension Dictionary {
    func mergedWith(otherDictionary: [Key: Value]) -> [Key: Value] {
        var mergedDict: [Key: Value] = [:]
        [self, otherDictionary].forEach { dict in
            for (key, value) in dict {
                mergedDict[key] = value
            }
        }
        return mergedDict
    }
}

그런 다음 사용법 은 다음과 같이 간단 합니다.

var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]

var combinedDict = dict1.mergedWith(dict2)
// => ["a": "foo", "b": "bar"]

더 편리한 기능 을 포함하는 프레임 워크를 선호한다면 HandySwift 를 확인 하십시오 . 그냥 프로젝트로 가져올 당신은 위의 코드를 사용할 수 있는 확장 기능을 추가하지 않고 프로젝트를 자신에게.


단일 기능을 사용하기 위해 라이브러리를 설치하는 것은 좋지 않습니다
HackaZach

@HackaZach :이 작은 부분 만 필요한 경우 전체 라이브러리가 포함되지 않도록 프레임 워크의 적절한 부분을 포함하도록 답변을 업데이트했습니다. 여러 기능을 사용하려는 사람들을 위해 프레임 워크에 힌트를 제공하고 있습니다. 이것이 좋은 연습을 유지하는 데 도움이되기를 바랍니다!
Jeehut

3

더 이상 확장이나 추가 기능이 필요하지 않습니다. 당신은 그렇게 쓸 수 있습니다 :

firstDictionary.merge(secondDictionary) { (value1, value2) -> AnyObject in
        return object2 // what you want to return if keys same.
    }

2

당신이 사용할 수있는,

func addAll(from: [String: Any], into: [String: Any]){
    from.forEach {into[$0] = $1}
}

1

bridgeToObjectiveC () 함수를 사용하여 사전을 NSDictionary로 만들 수 있습니다.

다음과 같습니다.

var dict1 = ["a":"Foo"]
var dict2 = ["b":"Boo"]

var combinedDict = dict1.bridgeToObjectiveC()
var mutiDict1 : NSMutableDictionary! = combinedDict.mutableCopy() as NSMutableDictionary

var combineDict2 = dict2.bridgeToObjectiveC()

var combine = mutiDict1.addEntriesFromDictionary(combineDict2)

그런 다음 NSDictionary (combine)를 다시 변환하거나 무엇이든 할 수 있습니다.


정확히 무슨 뜻인가요?
Anton

단지 환경 ​​설정입니다. 언어 사이를 연결하기 위해 복잡한 것 같습니다. obj-c가 더 빨리 죽게하면서 동시에 단일 언어의 한계에 충실하는 것이 좋습니다.
TruMan1

2
네, Swift가 발표 한 날에이 답변을 문자 그대로 올렸습니다 ....... 이유가있었습니다
Anton

1
import Foundation

let x = ["a":1]
let y = ["b":2]

let out = NSMutableDictionary(dictionary: x)
out.addEntriesFromDictionary(y)

결과는 Swift 유형 사전이 아닌 NSMutableDictionary이지만이 구문을 사용하는 구문은 동일 out["a"] == 1하므로 ( 이 경우) Swift 사전을 기대하는 타사 코드를 사용하는 경우에만 문제가 발생합니다. 유형 검사가 필요합니다.

여기서 짧은 대답은 실제로 반복해야한다는 것입니다. 명시 적으로 입력하지 않더라도 호출하는 메소드 (addEntriesFromDictionary : here)가 수행하는 방식입니다. 왜 그런지 잘 모르겠다면 두 개의 B-tree의 리프 노드를 병합하는 방법을 고려해야합니다.

실제로 Swift 기본 사전 유형이 실제로 필요한 경우 다음을 제안합니다.

let x = ["a":1]
let y = ["b":2]

var out = x
for (k, v) in y {
    out[k] = v
}

이 방법의 단점은 루프에서 사전 색인이 여러 번 재 빌드 될 수 있으므로 실제로 NSMutableDictionary 방식보다 약 10 배 느리다는 것입니다.


1

이 모든 응답은 복잡합니다. 이것은 신속한 2.2에 대한 나의 해결책입니다.

    //get first dictionnary
    let finalDictionnary : NSMutableDictionary = self.getBasicDict()
    //cast second dictionnary as [NSObject : AnyObject]
    let secondDictionnary : [NSObject : AnyObject] = self.getOtherDict() as [NSObject : AnyObject]
    //merge dictionnary into the first one
    finalDictionnary.addEntriesFromDictionary(secondDictionnary) 

불행히도 네이티브 Swift 사전이 아닌 NSMutableDictionary 에서만 작동합니다 . 이것이 기본적으로 Swift에 추가되기를 바랍니다.
Chris Paveglio

0

내 요구는 달랐으며, 클로버 링없이 불완전한 중첩 데이터 세트를 병합해야했습니다.

merging:
    ["b": [1, 2], "s": Set([5, 6]), "a": 1, "d": ["x": 2]]
with
    ["b": [3, 4], "s": Set([6, 7]), "a": 2, "d": ["y": 4]]
yields:
    ["b": [1, 2, 3, 4], "s": Set([5, 6, 7]), "a": 2, "d": ["y": 4, "x": 2]]

이것은 내가 원했던 것보다 더 힘들었습니다. 문제는 동적 타이핑에서 정적 타이핑으로 매핑하는 것이 었으며,이를 해결하기 위해 프로토콜을 사용했습니다.

또한 사전 리터럴 구문을 사용할 때 실제로 프로토콜 확장을 선택하지 않는 기본 유형을 얻습니다. 수집 요소의 균일 성을 검증하기가 쉽지 않기 때문에이를 지원하려는 노력을 중단했습니다.

import UIKit


private protocol Mergable {
    func mergeWithSame<T>(right: T) -> T?
}



public extension Dictionary {

    /**
    Merge Dictionaries

    - Parameter left: Dictionary to update
    - Parameter right:  Source dictionary with values to be merged

    - Returns: Merged dictionay
    */


    func merge(right:Dictionary) -> Dictionary {
        var merged = self
        for (k, rv) in right {

            // case of existing left value
            if let lv = self[k] {

                if let lv = lv as? Mergable where lv.dynamicType == rv.dynamicType {
                    let m = lv.mergeWithSame(rv)
                    merged[k] = m
                }

                else if lv is Mergable {
                    assert(false, "Expected common type for matching keys!")
                }

                else if !(lv is Mergable), let _ = lv as? NSArray {
                    assert(false, "Dictionary literals use incompatible Foundation Types")
                }

                else if !(lv is Mergable), let _ = lv as? NSDictionary {
                    assert(false, "Dictionary literals use incompatible Foundation Types")
                }

                else {
                    merged[k] = rv
                }
            }

                // case of no existing value
            else {
                merged[k] = rv
            }
        }

        return merged
    }
}




extension Array: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Array {
            return (self + right) as? T
        }

        assert(false)
        return nil
    }
}


extension Dictionary: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Dictionary {
            return self.merge(right) as? T
        }

        assert(false)
        return nil
    }
}


extension Set: Mergable {

    func mergeWithSame<T>(right: T) -> T? {

        if let right = right as? Set {
            return self.union(right) as? T
        }

        assert(false)
        return nil
    }
}



var dsa12 = Dictionary<String, Any>()
dsa12["a"] = 1
dsa12["b"] = [1, 2]
dsa12["s"] = Set([5, 6])
dsa12["d"] = ["c":5, "x": 2]


var dsa34 = Dictionary<String, Any>()
dsa34["a"] = 2
dsa34["b"] = [3, 4]
dsa34["s"] = Set([6, 7])
dsa34["d"] = ["c":-5, "y": 4]


//let dsa2 = ["a": 1, "b":a34]
let mdsa3 = dsa12.merge(dsa34)
print("merging:\n\t\(dsa12)\nwith\n\t\(dsa34) \nyields: \n\t\(mdsa3)")

0

스위프트 2.2

func + <K,V>(left: [K : V], right: [K : V]) -> [K : V] {
    var result = [K:V]()

    for (key,value) in left {
        result[key] = value
    }

    for (key,value) in right {
        result[key] = value
    }
    return result
}

당신이 이것을 넣으면 처음 루프를 제거 할 수 있습니다 :`var에 결과 = left`
NikeAlive

0

나는 단지 달러 라이브러리를 사용할 것 입니다.

https://github.com/ankurp/Dollar/#merge---merge-1

모든 사전을 병합하고 후자의 사전은 주어진 키의 값을 대체합니다.

let dict: Dictionary<String, Int> = ["Dog": 1, "Cat": 2]
let dict2: Dictionary<String, Int> = ["Cow": 3]
let dict3: Dictionary<String, Int> = ["Sheep": 4]
$.merge(dict, dict2, dict3)
=> ["Dog": 1, "Cat": 2, "Cow": 3, "Sheep": 4]

5
jQuery가 돌아왔다!
Ben Sinclair

0

내가 쓴 멋진 확장은 다음과 같습니다.

extension Dictionary where Value: Any {
    public func mergeOnto(target: [Key: Value]?) -> [Key: Value] {
        guard let target = target else { return self }
        return self.merging(target) { current, _ in current }
    }
}

쓰다:

var dict1 = ["cat": 5, "dog": 6]
var dict2 = ["dog": 9, "rodent": 10]

dict1 = dict1.mergeOnto(target: dict2)

그런 다음 dict1은 다음과 같이 수정됩니다.

["cat": 5, "dog": 6, "rodent": 10]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.