アイテムの辞書を別の辞書に追加するには


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
ああ、私はこれのための適切なジェネリック宣言を見つけることに非常に苦労しました、私はこれ以外のすべてを試しました。しかし、あなたはドロップすることができます、@assignmentそしてreturnあなたはすでに左に変異しています。編集:実際には、エラーは発生しませんが、そのままにして@assignmentおくべきです。
Roland

14
より多くの構文シュガー: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
Ivan Vavilov、2015

48
@animal_chin言語の半分を自分で実装する必要があるからですか?はい。感動しました。誤解しないでください。オペレーターのオーバーロードが大好きです。私はちょうどに建設されなければならない基本的な機能のためにそれを使用する必要が愛していない。
devios1

2
母@deviosそれスウィフトレポにプル要求します:明らかにAppleはarsedすることはできませんので、D
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
//可変:var dictA = ["x":1、 "y":2、 "z":3] var dictB = ["x":11、 "y":22、 "w":0] dictA。 merge(dictB、uniquingKeysWith:{(first、_)in first})print(dictA)// ["x":1、 "y":2、 "z":3、 "w":0]
muthukumar

1
この回答に示されている2番目の例は、と同等です[NSMutableDictionary addEntriesFromDictionary:]
orj 2018

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標準ライブラリリファレンスを見て 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 2014年

76

Swift 4はを提供するmerging(_:uniquingKeysWith:)ので、あなたの場合:

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

省略形のクロージャはを返す$1ため、キーとの競合がある場合、dict2の値が使用されます。


1
これが私が見つけた最も簡潔で最も近いものであることを指摘したいと思った- (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;。重複の処理方法については、「両方の辞書に同じキーが含まれている場合、そのキーの受信側辞書の以前の値オブジェクトにリリースメッセージが送信され、新しい値オブジェクトがその場所に置かれる」と記載されています。 Swiftバージョン、またはmerge(_:uniquingKeysWith :)では、2番目の値を返すの$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
+ =を使用して、(opの質問に従って)dictをインプレースで更新することもできます。
Rod

3
パラメータをasとして宣言し、そこから値をコピーするだけでmap、最初のfor (k, v)...ループを削除して削除できます。leftvarright
ネイトクック2014年

2
ディクショナリを変更する@NateCookは、+中置演算子の予期しない動作です。
mythz 2014年

それをありがとう。あなたの答えはおそらく私が投稿したサンプルコードに対してより正確でしたが、もう1つは私の質問に基づいて私が欲しかったものです。私の悪い、とにかく両方に
賛成票

2
@mythz +演算子のオーバーロードはどちらのメソッドでもないため、実際には変化しませんDictionary。単純な関数です。変数leftパラメーターに加えた変更は、関数の外部には表示されません。
ネイト・クック

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

16

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

そのような非変更関数から変更関数を呼び出すことはできません
njzk2

unionこの関数は、それが可能に渡された値があるvar変異させることができ、コピーされた辞書を意味します。func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }1行だけの場合は、よりも少しクリーンです。
MaddTheSane 2016

2
var paramsは非推奨であり、Swift 3で削除されます。これを行うための推奨方法は、本文でvarを宣言することですvar dictionary = dictionary。ここから:github.com/apple/swift-evolution/blob/master/proposals/...
ダニエル・ウッド

タイプセーフなものにする<Key, Value>ために、それらに追加しDictionaryます。
ラファエル

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
これがデフォルトでSwiftに組み込まれていない理由がわかりませんか?
ioquatix 2016

1
「不変」ソリューションで右からオーバーライドするために左から値を意図していますか?私はあなたが持っていることを意味すると思いますright.reduce(left)、少なくともそれは予想される動作imoです(そしてそれはあなたの2番目の例の動作です)-すなわち。["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 }
アンドレイ

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
2015年

1
dは、reduceブロックの各反復の永続的な結果であり、pは、削減されるコレクションの要素です。
farhadf 2015年

1
これは
Swift

varパラメータはswift 3で非推奨になりました
Dmitry Klochkov

これは、ここで説明したソリューションの中で私のお気に入りのソリューションです。優れた簡潔なソリューションのために、勝利をフィルター/マップ/リデュースしてください。
gokeji

7

SwifterSwift Libraryをお勧めします。ただし、ライブラリ全体とそのすべての優れた追加機能を使用したくない場合は、Dictionaryの拡張機能を利用できます。

Swift 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 2017

5

KeyValueの組み合わせを反復処理して、マージする値を取得し、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

Swift 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.
    }


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)を元に戻すか、何でもできます。


すみません、正確にはどういう意味ですか?
アントン

ただの好み。言語間を橋渡しするように複雑に思われる。単一の言語の範囲内に留まる方が良いと同時に、obj-cをより速く死なせます。
TruMan1 2016

2
ええ、スウィフトが発表した日に文字通りこの回答を投稿しました。......だから理由がありました
アントン

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)がそれを行います。2つのBツリーのリーフノードをどのようにマージするかを検討する必要がある場合に、その理由が少し不明確な場合はお勧めします。

実際に実際にSwiftネイティブ辞書タイプが必要な場合は、以下をお勧めします。

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

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

このアプローチの欠点は、ディクショナリインデックスが(ただし完了しても)ループ内で数回再構築される可能性があるため、実際にはNSMutableDictionaryアプローチよりも約10倍遅いということです。


1

これらの応答はすべて複雑です。これはSwift 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) 

これはNSMutableDictionaryでのみ機能し、残念ながらネイティブのSwift辞書では機能しません。これがSwiftにネイティブに追加されることを望みます。
クリスパヴェリオ2018

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

Swift 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 result = 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 2016

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.