Получение объединения двух карт на ходу

81

У меня есть рекурсивная функция, которая создает объекты, представляющие пути к файлам (ключи - это пути, а значения - информация о файле). Это рекурсивно, поскольку предназначено только для обработки файлов, поэтому, если обнаружен каталог, функция рекурсивно вызывается в каталоге.

Все, что было сказано, я хотел бы сделать эквивалент объединения наборов на двух картах (то есть «основная» карта, обновленная значениями из рекурсивного вызова). Есть ли идиоматический способ сделать это, кроме итерации по одной карте и присвоения каждого ключа, значения в нем тому же самому элементу на другой карте?

То есть: с учетом a,bимеют тип map [string] *SomeObject, а aи bв конечном счете населены, есть ли способ обновления aсо всеми значениями в b?

Джеффкнупп
источник
2
Возможно, вы сможете использовать для этого типа работы реальный контейнер набора: github.com/deckarep/golang-set
Ralph Caraveo
Предложение Ральфа подходит для наборов. Однако я бы сказал, что в вашем случае это не столько объединение, сколько слияние ; набор должен быть просто набором «ключей», в то время как у вас есть два набора пар ключ-значение, где один «набор» должен иметь приоритет над другим.
ANisus

Ответы:

132

В стандартных пакетах нет встроенного способа или какого-либо метода для такого слияния.

Идоматический способ - просто повторить:

for k, v := range b {
    a[k] = v
}
Анисус
источник
5
Чтобы добавить к тому, что ответил ANisus: Карты - это, по сути, хеш-таблицы. Вероятно, нет никакого способа вычислить объединение двух карт быстрее, чем просто итерация по обеим картам.
fuz
Вероятно, вы могли бы использовать отражение для написания функции объединения, не зависящей от типа, но это будет медленнее.
Эван
Разве этот код не должен UNION значения a [k] и v перед присвоением v a [k]? Что, если a [k] и v - массивы или карты?
vdolez
2
Он хочет объединить карты, а не обязательно значения на картах. Если вы хотите сделать что-то подобное, вам просто нужно изменить a[k] = vна a[k] = a[k] + vили что-то в этом роде.
Kyle
@ Кайл, я думаю, ты прав. Для фактического объединения это может быть использовано:a[k] = append(a[k], v...)
user3405291
2

Если у вас есть несколько вложенных карт, leftи rightэта функция будет рекурсивно добавлять элементы из rightв left. Если ключ уже leftвведен, мы углубляемся в структуру и пытаемся только добавить ключи left(например, никогда не заменять их).


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
}

ПРИМЕЧАНИЕ. Я не рассматриваю случаи, когда само значение не является a map[string]interface{}. Так что, если у вас есть, left["x"] = 1а right["x"] = 2затем приведенный выше код вызовет панику при попытке leftVal.(m).

JnBrymn
источник