Conseguir la unión de dos mapas en marcha

81

Tengo una función recursiva que crea objetos que representan rutas de archivo (las claves son rutas y los valores son información sobre el archivo). Es recursivo ya que solo está destinado a manejar archivos, por lo que si se encuentra un directorio, la función se llama de forma recursiva en el directorio.

Dicho todo esto, me gustaría hacer el equivalente a una unión de conjuntos en dos mapas (es decir, el mapa "principal" actualizado con los valores de la llamada recursiva). ¿Hay alguna forma idiomática de hacer esto además de iterar sobre un mapa y asignar cada clave, valor en él a lo mismo en el otro mapa?

Es decir: dado a,bson de tipo map [string] *SomeObject, y ay bson finalmente pobladas, ¿hay alguna manera de actualización acon todos los valores de b?

jeffknupp
fuente
2
Quizás pueda utilizar un contenedor de conjunto real para este tipo de trabajo: github.com/deckarep/golang-set
Ralph Caraveo
La sugerencia de Ralph es buena para los sets. Sin embargo, yo diría que en su caso no se trata tanto de una unión como de una fusión ; un conjunto debería ser simplemente una colección de "claves", mientras que usted tiene dos colecciones de pares clave-valor, donde un "conjunto" debería tener prioridad sobre el otro.
ANisus

Respuestas:

132

No hay una forma incorporada, ni ningún método en los paquetes estándar para hacer tal fusión.

La forma idomática es simplemente iterar:

for k, v := range b {
    a[k] = v
}
ANiso
fuente
5
Para agregar a lo que respondió ANisus: los mapas son esencialmente tablas hash. Es probable que no haya ninguna forma de calcular la unión de dos mapas más rápido que simplemente iterando sobre ambos mapas.
fuz
Probablemente podría usar la reflexión para escribir una función de unión agnóstica de tipo, pero sería más lenta.
Evan
¿No debería este código UNION el valor de a [k] yv antes de asignar v a a [k]? ¿Qué pasa si a [k] yv son matrices o mapas?
vdolez
2
Está buscando unir los mapas, no necesariamente los valores en los mapas. Si usted quiere hacer algo por el estilo, sólo tiene que cambiar a[k] = va a[k] = a[k] + vo algo por el estilo.
Kyle
@Kyle, creo que tienes razón. Para la unión real , esto se puede utilizar:a[k] = append(a[k], v...)
user3405291
2

Si tiene un par de mapas anidados lefty right, esta función agregará recursivamente los elementos de righten left. Si la clave ya está leftingresada, recurrimos más profundamente a la estructura e intentamos solo agregar claves left(por ejemplo, nunca reemplazarlas).


type m = map[string]interface{}

// Given two maps, recursively merge right into left, NEVER replacing any key that already exists in left
func mergeKeys(left, right m) m {
    for key, rightVal := range right {
        if leftVal, present := left[key]; present {
            //then we don't want to replace it - recurse
            left[key] = mergeKeys(leftVal.(m), rightVal.(m))
        } else {
            // key not in left so we can just shove it in
            left[key] = rightVal
        }
    }
    return left
}

NOTA: No manejo el caso en el que el valor no sea en sí mismo a map[string]interface{}. Así que si usted tiene left["x"] = 1y right["x"] = 2entonces el código anterior en pánico cuando se intenta leftVal.(m).

JnBrymn
fuente