web-dev-qa-db-fra.com

Comment fusionner deux cartes en STL et appliquer une fonction pour les conflits?

J'ai lu la question Fusionner deux STL cartes, et bien qu'elle soit proche, je cherchais des fonctionnalités comme celle décrite ici .

En bref, je voudrais fusionner deux std::map instances (ayant la même clé et le même type de valeur) en une seule, avec la mise en garde que je voudrais ajouter les valeurs ensemble si l'objet existe dans les deux cartes.

Existe-t-il une fonction boost , range-v ou std existante qui peut le faire? Et sinon, quelle serait la meilleure façon d'y parvenir?

Exemple de code:

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

Je ne connais aucune fonction existante pour cela, mais vous pouvez rouler la vôtre à partir de quelque chose de similaire à l'implémentation std :: merge pour avoir une complexité linéaire:

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

Démo

21
Jarod42

Je ne connais aucune fonction existante pour le faire, mais vous pouvez utiliser std::map --- merge fonction ( exemple en direct ):

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

En prime, l'implémentation de merge est plutôt efficace par rapport à ce que vous pouvez faire à la main à moins que vous ne la réimplémentiez à l'aide de extract. Au lieu de copier ou de déplacer des éléments, il ajuste les pointeurs internes pour déplacer les nœuds d'une carte à l'autre. Cependant, cela signifie qu'il modifie l'autre carte. Comme suggéré, le paramètre est pris par valeur, de sorte que l'autre carte peut être déplacée si elle n'est plus nécessaire et copiée autrement.

8
chris

Pour ce cas spécifique, car operator[] crée une clé si elle n'existe pas, vous pouvez utiliser une boucle simple pour ajouter les deux valeurs:

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

Et lorsque vous souhaitez utiliser une fonction, mais il est correct d'utiliser une valeur initialisée par défaut où aucune clé n'existe:

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