Başka bir sözlüğe nasıl bir Sözlük sözlüğü eklersiniz


172

Swift'teki diziler, bir Dizinin içeriğini diğerine eklemek için + = operatörünü destekler. Bir sözlük için bunu yapmanın kolay bir yolu var mı?

Örneğin:

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

Yanıtlar:


171

Sen tanımlayabilirsiniz +=için operatörü Dictionary, örneğin,

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

1
Ah adamım, bunun için uygun jenerik beyanı bulmakla çok uğraştım, bunun dışında her şeyi denedim. Ama bırakabilirsin, @assignmentve returnzaten mutasyona uğramışsın. Düzenleme: aslında, hiçbir hata almama rağmen, ben @assignmentkalmak gerektiğini düşünüyorum .
Roland

14
Daha fazla sözdizimi şekeri: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
Ivan Vavilov

48
@animal_chin Çünkü dilin yarısını kendimiz uygulamak zorundayız? Evet. Etkiledi. Beni yanlış anlamayın, operatörün aşırı yüklenmesini seviyorum. Sadece inşa edilmesi gereken temel özellikler için kullanmak zorunda
kalmayı sevmiyorum

2
@devios Haha sonra Swift repo için bir çekme isteği yapmak: D Belli ki Apple
silahlanamaz

6
Doğrudan SwifterSwift Kütüphanesinden çekiliyor :public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }
Justin Oroz

99

Swift 4'te şunlar kullanılmalıdır merging(_:uniquingKeysWith:):

Misal:

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
// değiştirilebilir: var dictA = ["x": 1, "y": 2, "z": 3] var dictB = ["x": 11, "y": 22, "w": 0] dictA. merge (dictB, uniquingKeysWith: {(önce ilk, _)} yazdır (dictA) // ["x": 1, "y": 2, "z": 3, "w": 0]
muthukumar

1
Bu cevapta gösterilen ikinci örnek eşdeğeridir [NSMutableDictionary addEntriesFromDictionary:].
orj

92

Peki ya

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

Bu, dict2'nin tüm anahtarlarını ve değerlerini dict1'e ekler.


43
Güzel çözüm. Biraz daha kısa: dict2.forEach {dict1 [$ 0] = 1 $}
Brett

1
Bu harika bir çözüm, ancak Swift 4 için büyük olasılıkla bir hata alırsınız Closure tuple parameter '(key: _, value: _)' does not support destructuring(en azından bu yazı sırasında).
Kapağın

78

Şu anda, Sözlük için Swift Standart Kütüphane Referansı'na baktığınızda, bir sözlüğü başka bir sözlükle kolayca güncellemenin bir yolu yoktur.

Bunu yapmak için bir uzantı yazabilirsiniz

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
Bu Sözlük için uzantı büyük bir kullanımıdır!
Marc Attinasi

76

Swift 4 , merging(_:uniquingKeysWith:)sizin durumunuz için şunları sağlar:

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

Steno kapanması geri döner $1, bu nedenle anahtarlarla çakışma olduğunda dict2'nin değeri kullanılır.


1
Sadece bunun Apple belgelerinin neyi ifade ettiğini bulduğum en özlü ve en yakın olduğunu belirtmek istedim - (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;. Yinelemelerle ne yapılacağı ile ilgili olarak, "Her iki sözlük de aynı anahtarı içeriyorsa, o anahtar için alıcı sözlüğün önceki değer nesnesine bir yayın iletisi gönderilir ve yeni değer nesnesi onun yerini alır." Swift sürümü veya birleştirme işleminde (_: uniquingKeysWith :), ikinci değeri döndürür, $1ne olduğu ile aynıdır addEntriesFromDictionary.
Tim Fuqua

31

Swift kitaplığında yerleşik değildir, ancak operatör aşırı yüklenmesi ile istediğinizi ekleyebilirsiniz, örn:

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
}

Bu +, artık operatöre sözlük eklemek için kullanabileceğiniz Sözlükler için operatörü aşırı yükler +, örneğin:

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

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

1
Ayrıca, bir dikteyi güncellemek için + = için de yapabilirsiniz (op sorusuna göre).
Rod

3
Parametreyi şu şekilde bildirirseniz ve mapilk değerleri ile birlikte kopyalarsanız ilk for (k, v)...döngüyü kaldırabilir ve bırakabilirsiniz . leftvarright
Nate Cook

2
@ Infate +operatörü için beklenen davranış olmayan Sözlüğü değiştiren @NateCook .
mythz

Bunun için teşekkürler. Cevabınız muhtemelen gönderdiğim örnek kod için daha doğruydu, diğeri soruma dayanarak istediğim şeydi. Benim kötü, neyse size ikisini de bir oy verdi;)
rustyshelf

2
@ mythz Gerçekten mutasyona uğramaz, çünkü +operatör aşırı yükü de bir yöntem değildir Dictionary, bu basit bir işlevdir. Değişken leftparametresinde yaptığınız değişiklikler , işlevin dışında görünmez.
Nate Cook

28

Hızlı 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
biraz daha iyifunc 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
    }
}

böyle bir mutasyona uğramayan fonksiyondan mutasyon fonksiyonu
çağıramazsınız

unionİşlevi bir olmak geçirilen değere sahip varmutasyona uğramış olabilir, kopyalanan sözlüğü anlamı. Sadece func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }bir satırdan biraz daha temiz .
MaddTheSane

2
var parametreler kaldırıldı ve bunu yapmanın tercih edilen bir şekilde vücutta bir değişken bildirmek için artık Swift 3'te kaldırılacak: var dictionary = dictionary. Buradan: github.com/apple/swift-evolution/blob/master/proposals/…
Daniel Wood

İşleri daha güvenli hale getirmek <Key, Value>için bunlara ekleyin Dictionary.
Raphael

12

değişmez

Ben değişmez sözlükleri +operatör ile birleştirmeyi / birleştirmeyi tercih ederim, bu yüzden şöyle uyguladım:

// 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"]

değişken

// 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
Bunun neden varsayılan olarak hızlı bir şekilde yerleşik olmadığını anlamıyorum?
ioquatix

1
"Değişmez" çözümünüzde sağını geçersiz kılmak için değerleri soldan mı bekliyorsunuz? Bence right.reduce(left)en azından beklenen davranış imo (yani ikinci örneğinizin davranışı) - yani. ["A":1] + ["A":2]gerekir çıktı["A":2]
ccwasden

Çıktı koda karşılık gelir. Başlangıç ​​değerinin şu anda olduğu gibi sağ taraf olmasını istiyorum.
ricardopereira

12

Artık sözlük uzantısına ihtiyacınız yok. Swift (Xcode 9.0+) sözlüğü bunun için bir işleve sahiptir. Buraya bir bak . Aşağıda bunun nasıl kullanılacağı ile ilgili bir örnek verilmiştir

  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
Yukarıdaki örnek için fonksiyonel bir stil ekliyorum:oldDictionary.merge(newDictionary) { $1 }
Andrej

11

Bir uzantı kullanılarak daha okunabilir bir varyant.

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
çok güzel ve temiz bir çözüm!
user3441734

11

Bunu deneyebilirsin

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

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

10

Bunları birleştirmek için azaltmayı da kullanabilirsiniz. Bunu oyun alanında deneyin

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
}

Bu ilginç görünüyor, ama nedir dve p?
soymak

1
d, indirgeme bloğunun her bir yinelemesinin devam eden sonucudur ve p, indirgenen koleksiyonun elementidir.
farhadf

1
Bu hızlı 3.0 beta çöküyor gibi görünüyor
possen


Burada bahsedilenlerden en sevdiğim çözüm bu. Mükemmel özlü çözümler için kazançları filtreleyin / haritalayın / azaltın.
gokeji

7

Ben tavsiye SwifterSwift Kütüphanesi . Ancak, tüm kütüphaneyi ve tüm harika eklemelerini kullanmak istemiyorsanız, Sözlük uzantılarını kullanabilirsiniz:

Hızlı 3+

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

Aslında, SE-110 geri döndürüldü, bu yüzden Swift 4 sürümü Swift 3 sürümü ile aynı olmalıdır.
BallpointBen

5

Anahtar Değer birleşimleri üzerinden birleştirmek istediğiniz değeri yineleyebilir ve updateValue (forKey :) yöntemi ile ekleyebilirsiniz:

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

Şimdi dictionaryTwo 'un tüm değerleri dictionaryOne' a eklendi.


4

@ Farhadf'ın cevabı ile aynıdır ancak Swift 3 için kabul edilmiştir:

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, sözlük uzantısı:

public extension Dictionary {

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

}

4

Swift 4 için bazı daha akıcı aşırı yükler:

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

Bunun gibi bir Dictionaryuzantı ekleyebilirsiniz :

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

O zaman kullanım aşağıdaki kadar basittir :

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

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

Ayrıca bazı içeren bir çerçeve tercih ederseniz daha kullanışlı özelliklere ödeme sonra HandySwift . Sadece projenize aktarın ve yukarıdaki kodu projeye herhangi bir uzantı eklemeden kullanabilirsiniz .


Tek bir işlevi kullanmak için bir kütüphane kurmak kötü bir uygulamadır
HackaZach

@HackaZach: Cevabımı, sadece bu küçük parçaya ihtiyaç duyulursa tüm kütüphanenin dahil edilmesini önlemek için çerçevenin uygun kısmını içerecek şekilde güncelledim. Birden fazla özelliğini kullanmak isteyen insanlar için çerçeveye ipucu veriyorum. Umarım bu iyi bir uygulama yapmaya yardımcı olur!
Jeehut

3

Artık uzatma veya ekstra fonk ihtiyacı yok. Böyle yazabilirsiniz:

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

2

Kullanabilirsiniz,

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

1

Sözlüğü NSDictionary yapmak için bridgeToObjectiveC () işlevini kullanabilirsiniz.

Aşağıdaki gibi olacak:

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)

Sonra NSDictionary (birleştirmek) geri dönüştürebilir veya her şeyi yapabilirsiniz.


Üzgünüm tam olarak ne demek istiyorsun?
Anton

Sadece bir tercih. Diller arasında köprü kurmak için kıvrımlı görünüyor. Aynı anda obj-c'nin daha hızlı ölmesine izin verirken, tek bir dilin sınırları içinde kalmak daha iyidir.
TruMan1

2
Evet, bu cevabı tam anlamıyla Swift'in ilan ettiği gün yayınladım ....... Bir sebep vardı
Anton

1
import Foundation

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

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

Sonuç, Swift yazılan bir sözlük değil, bir NSMutableDictionary olur, ancak sözdizimi aynıdır (out["a"] == 1 bu durumda), bu nedenle yalnızca bir Swift sözlüğü bekleyen üçüncü taraf kodu kullanıyorsanız veya gerçekten tip kontrolüne ihtiyacım var.

Buradaki kısa cevap, aslında ilmek yapmanız gerektiğidir. Açıkça girmeseniz bile, aradığınız yöntem (addEntriesFromDictionary: here) bunu yapar. İki B ağacının yaprak düğümlerini nasıl birleştireceğinizi düşünmeniz gereken durumun neden böyle olacağından biraz emin değilseniz öneririm.

Gerçekten bir Swift yerel sözlük türüne gerçekten ihtiyacınız varsa, öneririm:

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

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

Bu yaklaşımın dezavantajı, sözlük endeksinin - ancak ne olursa olsun - döngüde birkaç kez yeniden oluşturulabilmesidir, bu yüzden pratikte bu NSMutableDictionary yaklaşımından yaklaşık 10 kat daha yavaştır.


1

Tüm bu yanıtlar karmaşıktır. Bu hızlı 2.2 için benim çözüm:

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

Bu sadece NSMutableDictionary üzerinde çalışıyor , maalesef yerel Swift sözlüklerinde değil. Keşke bunun Swift'e doğal olarak eklenmesini isterdim.
Chris Paveglio

0

İhtiyaçlarım farklıydı, eksik iç içe veri kümelerini hızlandırmadan birleştirmem gerekiyordu.

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]]

Bu olmasını istediğimden daha zordu. Zorluk dinamik yazımdan statik yazmaya eşlemeliydi ve bunu çözmek için protokoller kullandım.

Ayrıca, sözlük değişmez sözdizimini kullandığınızda, aslında protokol uzantılarını almayan temel türlerini aldığınızdır. Koleksiyon öğelerinin tekdüzeliğini doğrulamak için kolay bulamadığım için bunları destekleme çabalarımı iptal ettim.

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

Hızlı 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
}

Eğer bunu koyarsanız ilk döngüyü kaldırabilirsiniz: `var sonuç = sol`
NikeAlive

0

Sadece Dolar kütüphanesini kullanýrým.

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

Tüm sözlükleri bir araya getirir ve ikinci sözlük verilen bir anahtardaki değeri geçersiz kılar

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 geri döndü!
Ben Sinclair

0

İşte yazdığım güzel bir uzantı ...

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

kullanmak:

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

dict1 = dict1.mergeOnto(target: dict2)

Ardından, dict1 şu şekilde değiştirilecektir:

["cat": 5, "dog": 6, "rodent": 10]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.