86 votes

Comment fusionner deux cartes STL ?

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

149voto

jkerian Points 8328

En supposant que vous voulez préserver les éléments dans mapA et de fusionner les éléments dans mapB pour laquelle il n'existe pas de clé dans mapA :

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

fera ce que vous voulez, je pense.

(EDIT : Si vous utilisez C++17 ou une version plus récente, tenez compte de cette réponse : https://stackoverflow.com/a/56594603/118150 )

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

0 votes

Je ne vois pas comment cela ne peut pas remplacer un duplicata dans mapA si les clés correspondent. Si je disais simplement que mapB était ma carte "préférée", je pourrais l'utiliser, je pense. De cette façon, s'il s'agit d'un doublon, la clé de mapB sera celle qui se retrouvera dans la nouvelle map (qui est maintenant mapA). Est-ce que cela semble correct ou est-ce que j'ai mal compris ce que fait l'insertion lorsqu'il y a un duplicata de E ?

15 votes

L'insertion n'écrase pas les éléments existants, lorsqu'il y a un conflit dans les clés, l'élément déjà existant a la priorité.

0 votes

Oh, je vois. Malheureusement, il ne se construit pas. Il génère un énorme message d'erreur

38voto

kiwibonga Points 53

Si vous souhaitez copier des entrées d'une carte à l'autre, vous pouvez utiliser la méthode suivante 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 effectuer une copie explicite, 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 une copie et une réécriture est de insert la cible dans la source et std::swap les résultats :

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

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

20voto

honk Points 4740

C++17

Comme mentionné dans Réponse de John Perry puisque C++17 std::map fournit un merge() fonction membre. Le site merge() produit le même résultat pour la carte cible que la fonction La solution de jkerian sur la base de l'utilisation insert() comme vous pouvez le voir dans l'exemple suivant, que j'ai emprunté à jkerian. J'ai juste mis à jour le code avec quelques C++11 et les caractéristiques de C++17 (telles que using alias de type , boucle for basée sur la gamme con 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 quand les touches se chevauchent. Si vous voulez que ce soit l'inverse, vous devez appeler bar.merge(foo); .

Cependant, il y a une différence entre l'utilisation insert() y merge() concernant ce qui arrive à la carte source. Le site insert() ajoute de nouvelles entrées à la carte cible, tandis que la fonction merge() déplace les entrées de la carte source. Cela signifie, pour l'exemple ci-dessus, que insert() ne modifie pas bar mais merge() supprime 4:40 de bar de sorte que seul 2:20 y 3:30 rester dans bar .

Note : J'ai réutilisé l'exemple de jkerian qui utilise map<int, int> pour des raisons de brièveté, mais merge() fonctionne également pour votre map<string, string> .

Code sur Coliru

18voto

John Perry Points 1341

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

3voto

Selon la norme ISO/IEC 14882:2003, section 23.1.2, tableau 69, expression a.insert(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'existe aucun élément dont la clé est équivalente à la clé clé de cet élément dans les conteneurs avec des clés uniques ;

Puisque cette std::map doit respecter cette restriction, si vous souhaitez donner la préférence aux "valeurs" d'une map par rapport à une autre, vous devez l'insérer dans celle-ci. Par exemple,

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

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X