Como você adiciona um dicionário de itens a outro dicionário

172

As matrizes no Swift oferecem suporte ao operador + = para adicionar o conteúdo de uma matriz a outra. Existe uma maneira fácil de fazer isso em um dicionário?

por exemplo:

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

var combinedDict = ... (some way of combining dict1 & dict2 without looping)
enferrujado
fonte
fromDict.forEach {intoDict[$0] = $1}
Sazzad Hissain Khan 14/11/19

Respostas:

171

Você pode definir um +=operador para Dictionary, por exemplo,

func += <K, V> (left: inout [K:V], right: [K:V]) { 
    for (k, v) in right { 
        left[k] = v
    } 
}
shucao
fonte
1
Oh, cara, eu lutei tanto em encontrar a declaração genérica apropriada para isso, tentei de tudo, exceto isso. Mas você pode soltar o @assignmente return, você já está mudando para a esquerda. Edit: na verdade, mesmo que eu não receba erros, acho que @assignmentdeveria ficar.
Roland
14
Mais açúcar de sintaxe: func +=<K, V> (inout left: [K : V], right: [K : V]) { for (k, v) in right { left[k] = v } }
Ivan Vavilov 05/02
48
@animal_chin Porque nós mesmos temos que implementar metade da linguagem? Sim. Impressionado. Não me interpretem mal, eu amo sobrecarregar o operador. Eu apenas não amo ter que usá-lo para características básicas que devem ser construídas dentro.
devios1
2
@devios Haha, em seguida, torná-lo um pedido puxar para o repo Swift: D Desde evidentemente, a Apple não pode ser arsed
CommaToast
6
Puxando direto da biblioteca SwifterSwift :public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach({ lhs[$0] = $1}) }
Justin Oroz
99

No Swift 4, deve-se usar merging(_:uniquingKeysWith:):

Exemplo:

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]
Vin Gazoil
fonte
1
// mutável: var dictA = ["x": 1, "y": 2, "z": 3] var dictB = ["x": 11, "y": 22, "w": 0] dictA. mesclar (dictB, uniquingKeysWith: {(first, _) in first}) print (dictA) // ["x": 1, "y": 2, "z": 3, "w": 0]
muthukumar
1
O segundo exemplo mostrado nesta resposta é equivalente a [NSMutableDictionary addEntriesFromDictionary:].
ORJ
92

E se

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

Isso adiciona todas as chaves e valores do dict2 no dict1.

jasongregori
fonte
43
Ótima solução. Um pouco mais curto: dict2.forEach {dict1 [$ 0] = $ 1}
Brett
1
Esta é uma ótima solução, mas para o Swift 4, você provavelmente receberá um erro informando Closure tuple parameter '(key: _, value: _)' does not support destructuring(pelo menos no momento da redação deste artigo). Seria necessário reestruturar o fechamento de acordo com [esta resposta do stackoverflow] ( stackoverflow.com/questions/44945967/… ):
JonnyB 4/17/17
78

Atualmente, observando a Referência da biblioteca padrão da Swift para o dicionário, não há como atualizar facilmente um dicionário com outro.

Você pode escrever uma extensão para fazer isso

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]
Cajado
fonte
3
Este é um ótimo uso da extensão para o dicionário!
Marc Attinasi
76

O Swift 4 fornece merging(_:uniquingKeysWith:), portanto, para o seu caso:

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

O fechamento abreviado retorna $1, portanto, o valor do dict2 será usado quando houver um conflito com as chaves.

samwize
fonte
1
Só queria ressaltar que este é o mais conciso e mais próximo que eu encontrei do que a documentação da Apple indica - (void)addEntriesFromDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;. Com relação ao que fazer com duplicatas, ele afirma que: "Se os dois dicionários contiverem a mesma chave, o objeto de valor anterior do dicionário receptor para essa chave receberá uma mensagem de liberação e o novo objeto de valor tomará seu lugar". a versão Swift, ou em mesclagem (_: uniquingKeysWith :), retornando o segundo valor,, $1é igual ao que addEntriesFromDictionaryfaz.
Tim Fuqua
31

Ele não está incorporado na biblioteca Swift, mas você pode adicionar o que deseja com a sobrecarga do operador, por exemplo:

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
}

Isso sobrecarrega o +operador de Dicionários, que agora você pode usar para adicionar dicionários ao +operador, por exemplo:

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

var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]
mythz
fonte
1
Você também pode fazê-lo para + = atualizar no local um ditado (conforme a pergunta operacional).
Rod
3
Você pode eliminar mape largar o primeiro for (k, v)...loop se declarar o leftparâmetro como vare depois copiar valores rightnele.
Nate Cook
2
@NateCook que alteraria o dicionário, o que não é um comportamento esperado para o +operador infix.
mythz
Obrigado por isso. Sua resposta provavelmente foi mais precisa para o código de amostra que eu publiquei, enquanto a outra era mais o que eu queria com base na minha pergunta. Meu mal, de qualquer maneira deu a ambos um voto positivo;)
rustyshelf
2
@mythz Não é realmente uma mutação, já que a +sobrecarga do operador também não é um método Dictionary, é uma função simples. As alterações feitas em um leftparâmetro variável não seriam visíveis fora da função.
Nate Cook
28

Swift 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"]
Pavel Sharanda
fonte
6
ligeiramente melhor #func merged(with dictionary: Dictionary<Key,Value>) -> Dictionary<Key,Value> { var copy = self dictionary.forEach { copy.updateValue($1, forKey: $0) } return copy }
Alexander Vasenin 03/10
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
    }
}
Olsen
fonte
não pode chamar uma função em mutação de uma função não-mutação como essa
njzk2
A unionfunção tem o valor passado para ser a var, o que significa que o dicionário copiado pode ser alterado. É um pouco mais limpo do que func union(dictionary: Dictionary) -> Dictionary { var dict2 = dictionary; dict2.unionInPlace(self); return dict2 }, mesmo que apenas por uma linha.
MaddTheSane
2
var params estão obsoletos e serão removidos em Swift 3. A maneira preferida do fazer é agora para declarar um var no corpo: var dictionary = dictionary. A partir daqui: github.com/apple/swift-evolution/blob/master/proposals/…
Daniel Wood
Para tornar as coisas mais seguras, digite- <Key, Value>as Dictionary.
Raphael
12

Imutável

Eu prefiro combinar / unir dicionários imutáveis ​​com +operador, então eu o implementei como:

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

Mutável

// 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"]
ricardopereira
fonte
5
Não entendo por que isso não é incorporado rapidamente por padrão?
21416 ioquatix
1
você pretende que os valores da esquerda substituam o direito em sua solução "Imutável"? Eu acho que você pretende ter right.reduce(left), pelo menos, esse é o comportamento esperado imo (e é o comportamento do seu segundo exemplo) - ie. ["A":1] + ["A":2]saída["A":2]
ccwasden
A saída corresponde ao código. Eu quero que o valor inicial seja o lado certo, como é agora.
Ricardopereira
12

Não há necessidade de extensões de dicionário agora. O dicionário Swift (Xcode 9.0+) possui uma funcionalidade para isso. Dê uma olhada aqui . Abaixo, aqui está um exemplo de como usá-lo

  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]
Vinayak Parmar
fonte
2
Estou adicionando um estilo funcional para o exemplo acima:oldDictionary.merge(newDictionary) { $1 }
Andrej
11

Uma variante mais legível usando uma extensão.

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
    }    
}
orkoden
fonte
3
solução muito agradável e limpa!
user3441734
11

Você pode tentar isso

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

var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)
Uttam Kumar
fonte
10

Você também pode usar reduzir para mesclá-los. Tente isso no parquinho

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
}
farhadf
fonte
Parece interessante, mas o que são de p?
rob
1
d é o resultado persistente de cada iteração do bloco de redução ep é o elemento da coleção que está sendo reduzida.
farhadf
1
isso parece travar no swift 3.0 beta
possen
parâmetros var estão obsoletos no swift 3
Dmitry Klochkov 27/10
Esta é a minha solução favorita dentre as mencionadas aqui. Filtre / mapeie / reduza os ganhos novamente para obter ótimas soluções concisas.
Gokeji
7

Eu recomendo a Biblioteca SwifterSwift . No entanto, se você não quiser usar a biblioteca inteira e todas as suas ótimas adições, basta usar a extensão do Dictionary:

Swift 3+

public extension Dictionary {
    public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
        rhs.forEach({ lhs[$0] = $1})
    }
}
Justin Oroz
fonte
Na verdade, o SE-110 foi revertido, portanto a versão Swift 4 deve ser a mesma que a versão Swift 3.
usar o seguinte
5

Você pode iterar sobre as combinações de valores-chave do valor que deseja mesclar e adicioná-las através do método updateValue (forKey :):

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

Agora todos os valores de dictionaryTwo foram adicionados ao dictionaryOne.

LeonS
fonte
4

O mesmo que a resposta de @ farhadf, mas adotada para o 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
}
Dmitry Klochkov
fonte
4

Swift 3, extensão de dicionário:

public extension Dictionary {

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

}
aaannjjaa
fonte
4

Algumas sobrecargas ainda mais simplificadas para o 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}
    }
}
John Montgomery
fonte
3

Você pode adicionar uma Dictionaryextensão como esta:

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

Então, o uso é tão simples quanto o seguinte:

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

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

Se você preferir uma estrutura que também inclua alguns recursos mais úteis , consulte o HandySwift . Basta importá-lo para o seu projeto e você pode usar o código acima sem adicionar extensões ao projeto.

Jeehut
fonte
Instalação de uma biblioteca para usar uma única função é uma prática ruim
HackaZach
@HackaZach: Acabei de atualizar minha resposta para incluir a parte apropriada da estrutura para impedir a inclusão de toda a biblioteca, se apenas essa pequena parte for necessária. Estou mantendo a dica para o framework para pessoas que desejam usar vários de seus recursos. Espero que isso ajude a manter boas práticas!
Jeehut
3

Não há necessidade de extensão ou qualquer função extra. Você pode escrever assim:

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

Você pode usar,

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

Você pode usar a função bridgeToObjectiveC () para tornar o dicionário um NSDictionary.

Será como o seguinte:

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)

Em seguida, você pode converter o NSDictionary (combinar) de volta ou fazer o que quiser.

Anton
fonte
Desculpe o que você quer dizer exatamente?
Anton
Apenas uma preferência. Parece complicado para fazer a ponte entre idiomas. É melhor ficar dentro dos limites de um único idioma, ao mesmo tempo em que deixa obj-c morrer mais rápido.
TruMan1
2
Sim, eu postei essa resposta literalmente no dia em que a Swift anunciou ....... Portanto, havia um motivo
Anton
1
import Foundation

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

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

O resultado é um NSMutableDictionary, não um dicionário do tipo Swift, mas a sintaxe para usá-lo é a mesma ( out["a"] == 1nesse caso); portanto, você só terá um problema se estiver usando um código de terceiros que espera um dicionário Swift ou realmente precisa da verificação de tipo.

A resposta curta aqui é que você realmente precisa fazer um loop. Mesmo se você não estiver inserindo explicitamente, é isso que o método que você está chamando (addEntriesFromDictionary: aqui) fará. Eu sugiro que, se você não souber ao certo por que seria esse o caso, considere como mesclaria os nós das folhas de duas árvores-B.

Se você realmente precisa de um tipo de dicionário nativo Swift em troca, sugiro:

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

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

A desvantagem dessa abordagem é que o índice do dicionário - por mais que seja feito - pode ser reconstruído várias vezes no loop, portanto, na prática, isso é cerca de 10 vezes mais lento que a abordagem NSMutableDictionary.

Jim Driscoll
fonte
1

Todas essas respostas são complicadas. Esta é a minha solução para o 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) 
Kevin ABRIOUX
fonte
Infelizmente, isso funciona apenas no NSMutableDictionary e não nos dicionários Swift nativos. Eu gostaria que isso fosse adicionado ao Swift nativamente.
Chris Paveglio
0

Minhas necessidades eram diferentes, eu precisava mesclar conjuntos de dados aninhados incompletos sem atrapalhar.

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

Isso foi mais difícil do que eu queria que fosse. O desafio estava no mapeamento da digitação dinâmica para a estática, e usei protocolos para resolver isso.

Também digno de nota é que quando você usa a sintaxe literal do dicionário, na verdade você obtém os tipos de fundação, que não capturam as extensões de protocolo. Abortei meus esforços para apoiá-los, pois não achei fácil validar a uniformidade dos elementos da coleção.

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
fonte
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
}
apinho
fonte
se você colocar isso, poderá remover o primeiro loop: `var result = left` #
NikeAlive
0

Eu usaria apenas o dólar biblioteca .

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

Mescla todos os dicionários e o último dicionário substitui o valor em uma determinada chave

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]
Andy
fonte
5
jQuery está de volta yay!
Ben Sinclair
0

Aqui está uma boa extensão que escrevi ...

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

usar:

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

dict1 = dict1.mergeOnto(target: dict2)

Em seguida, dict1 será modificado para

["cat": 5, "dog": 6, "rodent": 10]
Brian J. Roberts
fonte