¿Cómo combinar dos instancias de diccionario en Swift?

81

¿Cómo agrego uno Dictionarya otro Dictionaryusando Swift?

Estoy usando la AlamoFirebiblioteca para enviar una JSONa una REST server.

Diccionario 1

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

Diccionario 2

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

¿Cómo combino los dos diccionarios para crear un nuevo diccionario como se muestra a continuación?

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

Lo intenté dict1 += dict2pero obtuve un error de compilación:

El operador binario '+ =' no se puede aplicar a dos operandos '[String: AnyObject]'

El nómada
fuente
Hola @ the-nomad, ¿podrías mover tu respuesta aceptada a la que tiene más del doble de votos a favor, por favor :-)?
blackjacx

Respuestas:

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

Consulte el increíble proyecto Dollar & Cent https://github.com/ankurp/Cent/blob/master/Sources/Dictionary.swift

Shuo
fuente
¿Dónde puedo poner el extensionpara tener esto functiondisponible en cualquier lugar de mi código? ¡Lo siento, soy un novato!
The Nomad
1
Ponlo en cualquier archivo de tu proyecto
Shuo
¿Qué pasa si el valor también es un diccionario, cómo se fusiona en profundidad?
Rodrigo Ruiz
@RodrigoRuiz No creo que haga ninguna diferencia, siempre que su diccionario original también tenga la misma arquitectura, no hará una diferencia (a menos que el diccionario original sea de tipo [Key: Any])
Septronic
2
La extensión ya no es necesaria ya que Dictionarytiene sus propias funciones de combinación. Consulte stackoverflow.com/a/43615143/971329 .
blackjacx
154

Amo este enfoque:

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

Swift 4 y 5

Con Swift 4, Apple presenta un mejor enfoque para fusionar dos diccionarios:

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]

Aquí tiene 2 opciones (como con la mayoría de las funciones que operan en contenedores):

  • merge muta un diccionario existente
  • merging devuelve un nuevo diccionario
blackjacx
fuente
16

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

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

Swift 4 tiene una nueva función: let parameters = dict1.reduce(into: dict2) { (r, e) in r[e.0] = e.1 }

Es muy importante que cavar alrededor de la biblioteca estándar: map, reduce, dropFirst, forEachetc, son alimentos básicos de la escueta código. ¡Los bits funcionales son divertidos!

emp
fuente
El compilador Swift 2.2 muestra una advertencia: "Los parámetros 'var' están obsoletos y se eliminarán en Swift 3".
Dheeraj Vepakomma
7

SequenceType.forEach(implementado por Dictionary) proporciona una solución elegante para agregar elementos de un diccionario a otro diccionario.

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

Por ejemplo

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

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

    XCTAssertEqual(dic2[1], "foo")
}
rhooke
fuente
7
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
fuente
4

Podemos fusionar diccionarios de una mejor manera usando merge keyword

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
fuente
3

Mis necesidades eran diferentes, quería fusionarme y no golpear.

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

Esperaba una solución más simple, pero esto es con lo que terminé. El desafío consistía en pasar de la escritura dinámica a la escritura estática, y utilicé protocolos para resolver esto.

También es digno de mención que cuando usa la sintaxis literal del diccionario, en realidad obtiene los tipos de base, que no recogen las extensiones de protocolo. Aborté mis esfuerzos para apoyarlos ya que no pude encontrar una manera fácil de validar la uniformidad de los elementos de la colección.

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
fuente
2

Pruebe este enfoque

    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)

Imprimirá lo siguiente:

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

}

Espero eso ayude !!

Glotón
fuente
1

Puede usar el siguiente código para combinar dos instancias de diccionario en 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
fuente
-1

Está utilizando la palabra clave let para declarar el diccionario, por lo que no puede realizar cambios en su diccionario, ya que se utiliza para declarar constante.

Cámbielo a la palabra clave var, entonces funcionará para usted.

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

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

dict1 += dict2
Meenakshi
fuente