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


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.