web-dev-qa-db-ja.com

STLで2つのマップをマージし、競合の機能を適用するにはどうすればよいですか?

Merge two STL mapsの質問を読みましたが、近いですが、説明されているような機能を探していました here

要するに、2つのstd::mapインスタンス(同じキーと値の型を持つ)に1つ、オブジェクトが存在する場合は値を一緒に追加したいという警告両方のマップ。

これを行うことができる既存の boostrange-v 、または 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;
    }
}
30
nonsensickle

そのための既存の関数については知りませんが、 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());
}

デモ

21
Jarod42

これを行うための既存の関数は知りませんが、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's left in toMerge is conflicting keys
    for (const auto& [k, v] : toMerge) { 
        base[k] = combine(base[k], toMerge[k]);
    }
}

ボーナスとして、mergeの実装は、extractのようなものを使用して再実装しない限り、手動で実行できるものと比較してかなり効率的です。要素をコピーまたは移動する代わりに、内部ポインタを調整して、ノードをあるマップから別のマップに移動します。ただし、これは他のマップを変更することを意味します。提案されているように、パラメータは値によって取得されるため、もう必要ない場合は他のマップを移動し、それ以外の場合はコピーできます。

8
chris

この特定のケースでは、 operator[] は、キーが存在しない場合にキーを作成します。単純なループを使用して2つの値を追加できます。

for (const auto& pair : mapB) {
    mapA[pair.first] += pair.second;
}

関数を使用したいが、キーが存在しないデフォルトの初期化された値を使用してもよい場合:

for (const auto& pair : mapB) {
    mapA[pair.first] = mergePredicate(mapA[pair.first], pair.second);
}
6
Ville-Valtteri