web-dev-qa-db-ja.com

2つのSTLマップをマージするにはどうすればよいですか?

2つのSTLマップを1つにマージするにはどうすればよいですか?両方とも同じキーと値のタイプ(map<string, string>)。キーが重複している場合は、いずれかのマップを優先します。

61
JonF

mapAの要素を保持し、mapBにキーがないmapAの要素をマージするとします。

mapA.insert(mapB.begin(), mapB.end())

あなたが望むことをするでしょう。

作業例:

#include <iostream>
#include <map>

void printIt(std::map<int,int> m) {
    for(std::map<int,int>::iterator it=m.begin();it!=m.end();++it)
        std::cout << it->first<<":"<<it->second<<" ";
    std::cout << "\n";
}

int main() {
    std::map<int,int> foo,bar;
    foo[1] = 11; foo[2] = 12; foo[3] = 13;
    bar[2] = 20; bar[3] = 30; bar[4] = 40;
    printIt(foo);
    printIt(bar);
    foo.insert(bar.begin(),bar.end());
    printIt(foo);
    return 0;
}

出力:

:!./insert
1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40
113
jkerian

あるマップから別のマップにエントリをコピーする場合は、std::mapinsert

targetMap.insert(sourceMap.begin(), sourceMap.end());

ただし、キーがすでにtargetMapにある場合、insertは要素を更新しないことに注意してください。それらのアイテムはそのまま残されます。要素を上書きするには、明示的にコピーする必要があります。例:

for(auto& it : sourceMap)
{
    targetMap[it.first] = it.second;
}

sourceMapのデータを失うことを気にしない場合、コピーと上書きを実現する別の方法は、ターゲットをソースにinsertし、std::swap 結果:

sourceMap.insert(targetMap.begin(), targetMap.end());
std::swap(sourceMap, targetMap);

スワップ後、sourceMapにはtargetMapの古いデータが含まれ、targetMapsourceMapのエントリを優先して、2つのマップのマージになります。

29
kiwibonga

C++ 17以降、マップには merge() メソッドがあることに注意してください。

11
John Perry

ISO/IEC 14882:2003、セクション23.1.2、表69、式a.insert(i、j)によると:

pre:i、jはaへの反復子ではありません。一意のキーを持つコンテナにその要素のキーに相当するキーを持つ要素がない場合にのみ、範囲[i、j)から各要素を挿入します。

Std :: mapはこの制限に従う必要があるため、あるマップの「値」を別のマップよりも優先させる場合は、マップに挿入する必要があります。例えば、

std::map<int, int> goodKeys;
std::map<int, int> betterKeys;

betterKeys.insert(goodKeys.begin(), goodKeys.end());

したがって、goodKeysおよびbetterKeysに同等のキーがある場合、betterKeysの「値」は保持されます。

3

C++ 17

John Perry's answer で述べたように、 C++ 17std::mapmerge()を提供するため メンバー関数。 merge()関数は、以下からわかるように、 insert() の使用に基づいて jkerianの解 と同じ結果をターゲットマップに対して生成します。例、jkerianから借りました。 C++ 11 およびC++ 17の機能( using type aliasrange-based forループ with 構造化バインディング 、および リスト初期化 ):

using mymap = std::map<int, int>;

void printIt(const mymap& m) {
    for (auto const &[k, v] : m)
        std::cout << k << ":" << v << " ";
    std::cout << std::endl;
}

int main() {
    mymap foo{ {1, 11}, {2, 12}, {3, 13} };
    mymap bar{ {2, 20}, {3, 30}, {4, 40} };
    printIt(foo);
    printIt(bar);
    foo.merge(bar);
    printIt(foo);
    return 0;
}

出力:

1:11 2:12 3:13
2:20 3:30 4:40
1:11 2:12 3:13 4:40

ご覧のように、merge()は、キーがオーバーラップするときにターゲットマップfooを優先します。逆にしたい場合は、bar.merge(foo);を呼び出す必要があります。

ただし、ソースマップの処理に関してinsert()merge()の使用には違いがあります。 insert()関数は新しいエントリをターゲットマップに追加し、merge()関数はエントリをソースマップから移動します。つまり、上記の例では、insert()barを変更しませんが、merge()barから4:40を削除するため、2:203:30barに残ります。

注:簡潔にするためにmap<int, int>を使用するjkerianの例を再利用しましたが、merge()map<string, string>でも機能します。

Coliruのコード

1
honk