Jak połączyć dwie instancje słownika w Swift?

87

Jak dołączyć jeden Dictionarydo drugiego Dictionaryza pomocą Swift?

Korzystam z AlamoFirebiblioteki, aby wysłać plik JSONdo REST server.

Słownik 1

var dict1: [String: AnyObject] = [
        kFacebook: [
            kToken: token
        ]
    ]

Słownik 2

var dict2: [String: AnyObject] = [
        kRequest: [
            kTargetUserId: userId
        ]
    ]

Jak połączyć te dwa słowniki, aby utworzyć nowy słownik, jak pokazano poniżej?

let parameters: [String: AnyObject] = [
        kFacebook: [
            kToken: token
        ],
        kRequest: [
            kTargetUserId: userId
        ]
    ]

Próbowałem, dict1 += dict2ale wystąpił błąd kompilacji:

Operator binarny „+ =” nie może być zastosowany do dwóch operandów „[String: AnyObject]”

Nomad
źródło
Hej @ the-nomad, czy możesz przenieść swoją zaakceptowaną odpowiedź na tę, która ma więcej niż podwójne głosy za - proszę :-)?
blackjacx

Odpowiedzi:

67
var d1 = ["a": "b"]
var d2 = ["c": "e"]

extension Dictionary {
    mutating func merge(dict: [Key: Value]){
        for (k, v) in dict {
            updateValue(v, forKey: k)
        }
    }
}

d1.merge(d2)

Zapoznaj się z niesamowitym projektem Dollar & Cent https://github.com/ankurp/Cent/blob/master/Sources/Dictionary.swift

Shuo
źródło
gdzie mogę umieścić to, extensionaby było to functiondostępne w dowolnym miejscu mojego kodu? Przepraszam, jestem noobem!
The Nomad
1
Umieść to w dowolnym pliku w swoim projekcie
Shuo
Co jeśli wartość jest również słownikiem, jak głęboko scalisz?
Rodrigo Ruiz
@RodrigoRuiz Myślę, że to nie robi różnicy, o ile twój oryginalny słownik ma tę samą architekturę, to nie zrobi różnicy (chyba że oryginalny słownik jest typu [Key: Any])
Septronic
2
Rozszerzenie nie jest już potrzebne, ponieważ Dictionaryma własne funkcje scalania. Zobacz stackoverflow.com/a/43615143/971329 .
blackjacx
161

Uwielbiam to podejście:

dicFrom.forEach { (key, value) in dicTo[key] = value }

Swift 4 i 5

Dzięki Swift 4 Apple wprowadza lepsze podejście do łączenia dwóch słowników:

let dictionary = ["a": 1, "b": 2]
let newKeyValues = ["a": 3, "b": 4]

let keepingCurrent = dictionary.merging(newKeyValues) { (current, _) in current }
// ["b": 2, "a": 1]

let replacingCurrent = dictionary.merging(newKeyValues) { (_, new) in new }
// ["b": 4, "a": 3]

Masz tutaj 2 opcje (jak w przypadku większości funkcji działających na kontenerach):

  • merge mutuje istniejący słownik
  • merging zwraca nowy słownik
blackjacx
źródło
16

Dla Swift> = 2,2:
let parameters = dict1.reduce(dict2) { r, e in var r = r; r[e.0] = e.1; return r }

Dla Swift <2,2:
let parameters = dict1.reduce(dict2) { (var r, e) in r[e.0] = e.1; return r }

Swift 4 ma nową funkcję: let parameters = dict1.reduce(into: dict2) { (r, e) in r[e.0] = e.1 }

To naprawdę ważne, aby grzebać standardowej biblioteki: map, reduce, dropFirst, forEachitd. Są zszywki zwięzły kodu. Funkcjonalne bity są fajne!

emp
źródło
Kompilator Swift 2.2 wyświetla ostrzeżenie: „Parametry„ var ”są przestarzałe i zostaną usunięte w wersji Swift 3”
Dheeraj Vepakomma
8
func +=<Key, Value> (lhs: inout [Key: Value], rhs: [Key: Value]) {
    rhs.forEach{ lhs[$0] = $1 }
}

var dic1 = ["test1": 1]

dic1 += ["test2": 2]

dic1  // ["test2": 2, "test1": 1]
Leo Dabus
źródło
7

SequenceType.forEach(zaimplementowany przez Dictionary) zapewnia eleganckie rozwiązanie dodawania elementów słownika do innego słownika.

dic1.forEach { dic2[$0] = $1 }

Na przykład

func testMergeDictionaries() {
    let dic1 = [1:"foo"]
    var dic2 = [2:"bar"]

    dic1.forEach { dic2[$0] = $1 }

    XCTAssertEqual(dic2[1], "foo")
}
rhooke
źródło
6

Możemy lepiej łączyć słowniki za pomocą słowa kluczowego merge

var dictionary = ["a": 1, "b": 2]
/// Keeping existing value for key "a":
dictionary.merge(["a": 3, "c": 4]) { (current, _) in current }
 // ["b": 2, "a": 1, "c": 4]
Raja Jawahar
źródło
3

Moje potrzeby były inne, chciałem się scalić, a nie przebić.

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

Liczyłem na prostsze rozwiązanie, ale na tym skończyłem. Wyzwanie polegało na przeskakiwaniu od dynamicznego do statycznego pisania i użyłem protokołów do rozwiązania tego problemu.

Warto również zauważyć, że kiedy używasz składni literału słownikowego, w rzeczywistości otrzymujesz typy fundamentów, które nie odbierają rozszerzeń protokołu. Zrezygnowałem z wysiłków, aby je wspierać, ponieważ nie mogłem znaleźć łatwego sposobu na sprawdzenie jednolitości elementów kolekcji.

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)")
Chris Conover
źródło
2

Wypróbuj to podejście

    let dict1: [String: AnyObject] = ["kFacebook": ["kToken": "token"]]
    let dict2: [String: AnyObject] = ["kRequest": ["kTargetUserId": "userId"]]

    var combinedAttributes : NSMutableDictionary!

    combinedAttributes = NSMutableDictionary(dictionary: dict1)

    combinedAttributes.addEntriesFromDictionary(dict2)

    println(combinedAttributes)

Zostanie wydrukowany następujący:

{
kFacebook =     {
    kToken = token;
};
kRequest =     {
    kTargetUserId = userId;
};

}

Mam nadzieję, że to pomoże !!

Rosomak
źródło
1

Możesz użyć poniższego kodu, aby połączyć dwa wystąpienia słownika w Swift:

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
    }
}
Pallavi
źródło
-1

Używasz słowa kluczowego let, aby zadeklarować słownik, więc nie możesz wprowadzać zmian w słowniku, ponieważ jest używany do deklarowania stałej.

Zmień to na słowo kluczowe var, a będzie działać dla Ciebie.

var dict1: [String: AnyObject] = [
            kFacebook: [
                kToken: token
            ]
        ]

var dict2: [String: AnyObject] = [
        kRequest: [
            kTargetUserId: userId
        ]
    ]

dict1 += dict2
Meenakshi
źródło