web-dev-qa-db-fra.com

Comment fusionner deux cartes STL?

Comment fusionner deux cartes STL en une seule? Ils ont tous deux les mêmes types de clé et de valeur (map<string, string>). S'il y a un chevauchement des touches, je voudrais donner la préférence à l'une des cartes.

61
JonF

En supposant que vous souhaitiez conserver les éléments dans mapA et fusionner les éléments dans mapB pour lesquels il n'y a pas de clé dans mapA:

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

fera ce que vous voulez, je pense.

Exemple de travail:

#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;
}

sortie:

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

Si vous souhaitez copier des entrées d'une mappe vers une autre, vous pouvez utiliser std::map's insert:

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

Mais notez que insert ne met pas à jour les éléments si leur clé est déjà dans targetMap; ces éléments seront laissés tels quels. Pour écraser des éléments, vous devrez copier explicitement, par exemple:

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

Si cela ne vous dérange pas de perdre les données dans sourceMap, une autre façon de réaliser un copier-écraser est de insert la cible dans la source et std::swap Les resultats:

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

Après l'échange, sourceMap contiendra les anciennes données de targetMap et targetMap sera une fusion des deux cartes, avec une préférence pour les entrées de sourceMap.

29
kiwibonga

Notez que, depuis C++ 17, il existe une méthode merge() pour les cartes.

11
John Perry

Selon ISO/CEI 14882: 2003, section 23.1.2, tableau 69, expression a. Insérer (i, j):

pre: i, j ne sont pas des itérateurs dans a. insère chaque élément de la plage [i, j) si et seulement s'il n'y a pas d'élément avec une clé équivalente à la clé de cet élément dans des conteneurs avec des clés uniques;

Étant donné que std :: map doit suivre cette restriction, si vous souhaitez donner la préférence aux "valeurs" d'une carte par rapport à une autre, vous devez l'insérer. Par exemple,

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

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

Donc, s'il y a des clés équivalentes dans goodKeys et betterKeys, les "valeurs" des betterKeys seront préservées.

3

C++ 17

Comme mentionné dans réponse de John Perry , puisque C++ 17std::map fournit un merge() fonction membre. La fonction merge() produit le même résultat pour la carte cible que solution de jkerian basé sur l'utilisation de insert() , comme vous pouvez le voir sur l'exemple suivant, que j'ai emprunté à jkerian. Je viens de mettre à jour le code avec certaines fonctionnalités C++ 11 et C++ 17 (telles que using alias de type , basé sur la plage) pour la boucle avec liaison structurée , et initialisation de la liste ):

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;
}

Sortie:

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

Comme vous pouvez le voir, merge() donne également la priorité à la carte cible foo lorsque les touches se chevauchent. Si vous voulez l'avoir dans l'autre sens, alors vous devez appeler bar.merge(foo);.

Cependant, il existe une différence entre l'utilisation de insert() et merge() concernant ce qui arrive à la carte source. Les fonctions insert() ajoutent de nouvelles entrées à la carte cible, tandis que merge() déplace les entrées de la carte source. Cela signifie que pour l'exemple ci-dessus, insert() ne modifie pas bar, mais merge() supprime 4:40 De bar, de sorte que seuls 2:20 et 3:30 restent dans bar.

Remarque: J'ai réutilisé l'exemple de jkerian qui utilise map<int, int> Par souci de concision, mais merge() fonctionne également pour votre map<string, string>.

Code sur Colir

1
honk