Как объединить две карты в STL и применить функцию для конфликтов?
Я прочитал вопрос о слиянии двух STL- карт, и, хотя он близок, я искал функциональность, подобную описанной здесь.
Короче говоря, я хотел бы объединить два экземпляра std::map
(имеющих один и тот же ключ и тип значения) в один, с оговоркой, которую я хотел бы добавить вместе, если объект существует на обеих картах.
Есть ли существующая функция boost, range-v3 или std, которая может это сделать? А если нет, то какой был бы лучший способ достичь этого?
Пример кода:
double mergePredicate(double lhs, double rhs)
{
return lhs + rhs;
}
int main()
{
std::map<int, double> mapA = { {0, 1.0}, {1, 2.0} };
std::map<int, double> mapB = { {1, 1.5}, {2, 2.5} };
// Merge maps in some way...
merge(mapA, mapB, mergePredicate);
// result: mapA == { {0, 1.0}, {1, 3.5}, {2, 2.5} }
for (const auto& p : mapA) {
std::cout << p.first << " " << p.second << std::endl;
}
}
Ответы
Ответ 1
Я не знаю какой-либо существующей функции для этого, но вы можете свернуть свой собственный от чего-то подобного реализации std :: merge, чтобы иметь линейную сложность:
template<class Map, class Merger>
void merge(Map& dest, const Map& source, Merger merger)
{
auto it1 = dest.begin();
auto it2 = source.begin();
auto&& comp = dest.value_comp();
for (; it1 != dest.end() && it2 != source.end(); ) {
if (comp(*it1, *it2)) {
++it1;
} else if (comp(*it2, *it1)) {
dest.insert(it1, *it2); // with hint to have correct complexity
++it2;
} else { // equivalent
it1->second = merger(it1->second, it2->second);
++it1;
++it2;
}
}
dest.insert(it2, source.end());
}
демонстрация
Ответ 2
Я не знаю какой-либо существующей функции для этого, но вы можете использовать функцию std::map
merge
(живой пример):
template<typename K, typename V, typename F>
void mergeWithConflicts(std::map<K, V>& base, std::map<K, V> toMerge, F combine) {
base.merge(toMerge);
// All that left in toMerge is conflicting keys
for (const auto& [k, v] : toMerge) {
base[k] = combine(base[k], toMerge[k]);
}
}
В качестве бонуса реализация merge
довольно эффективна по сравнению с тем, что вы можете сделать вручную, если только вы не переопределите его, используя подобные extract
. Вместо копирования или перемещения элементов он настраивает внутренние указатели для перемещения узлов с одной карты на другую. Однако это означает, что он изменяет другую карту. Как и было предложено, параметр берется по значению, поэтому можно перемещать другую карту, если она больше не нужна и копируется в противном случае.
Ответ 3
Для этого конкретного случая, поскольку operator[]
создает ключ, если он не существует, вы можете использовать простой цикл для добавления двух значений:
for (const auto& pair : mapB) {
mapA[pair.first] += pair.second;
}
И когда вы хотите использовать функцию, но нормально использовать инициализированное по умолчанию значение, где не существует ключа:
for (const auto& pair : mapB) {
mapA[pair.first] = mergePredicate(mapA[pair.first], pair.second);
}