Как эффективно сравнивать две карты строк в С++

Мне интересно, возможно ли только при использовании некоторых стандартных алгоритмов написать короткую функцию, которая сравнивает два std::map<string, string> и возвращает true, если все пары ключ-значение (но некоторые) являются истинными.

Например, эти две карты должны оцениваться как равные

map<string,string> m1, m2;

m1["A"]="1";
m2["A"]="1";

m1["B"]="2";
m2["B"]="2";

m1["X"]="30";
m2["X"]="340";

m1["Y"]="53";
m2["Y"]="0";

Предположим, что обе карты имеют одинаковый размер, и все их элементы должны быть попарно сопоставлены, за исключением значения, сохраненного ключом "X" и ключом "Y". Первая попытка была бы очень неэффективной двойной вложенной петлей. Я уверен, что лучшее решение может быть достигнуто.

Ответ 1

Я не уверен, что именно вы ищете, поэтому позвольте мне сначала дать полное равенство, а затем ключевое равенство. Возможно, последнее уже соответствует вашим потребностям.

Полное равенство

(Хотя стандартную эквивалентность можно проверить с помощью собственных операторов сравнения std::map, в качестве основы для сравнения на основе значений можно использовать следующее.)

Полное равенство можно проверить с помощью std::equal и std::operator== для std::pair s:

#include <utility>
#include <algorithm>
#include <string>
#include <iostream>
#include <map>

template <typename Map>
bool map_compare (Map const &lhs, Map const &rhs) {
    // No predicate needed because there is operator== for pairs already.
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(),
                      rhs.begin());
}

int main () {
    using namespace std;

    map<string,string> a, b;

    a["Foo"] = "0";
    a["Bar"] = "1";
    a["Frob"] = "2";

    b["Foo"] = "0";
    b["Bar"] = "1";
    b["Frob"] = "2";

    cout << "a == b? " << map_compare (a,b) << " (should be 1)\n";
    b["Foo"] = "1";
    cout << "a == b? " << map_compare (a,b) << " (should be 0)\n";

    map<string,string> c;
    cout << "a == c? " << map_compare (a,c)  << " (should be 0)\n";
}

Ключевое равенство

C++ 2003

На основе приведенного выше кода мы можем добавить предикат к вызову std::equal:

struct Pair_First_Equal {
    template <typename Pair>
    bool operator() (Pair const &lhs, Pair const &rhs) const {
        return lhs.first == rhs.first;
    }
};

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) {
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(),
                      rhs.begin(),
                      Pair_First_Equal()); // predicate instance
}

int main () {
    using namespace std;

    map<string,string> a, b;

    a["Foo"] = "0";
    a["Bar"] = "1";
    a["Frob"] = "2";

    b["Foo"] = "0";
    b["Bar"] = "1";
    b["Frob"] = "2";

    cout << "a == b? " << key_compare (a,b) << " (should be 1)\n";
    b["Foo"] = "1";
    cout << "a == b? " << key_compare (a,b) << " (should be 1)\n";

    map<string,string> c;
    cout << "a == c? " << key_compare (a,c)  << " (should be 0)\n";
}

C++ (C++ 11)

Используя новые лямбда-выражения, вы можете сделать это:

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) {

    auto pred = [] (decltype(*lhs.begin()) a, decltype(a) b)
                   { return a.first == b.first; };

    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
}

C++ (C++ 14)

added 2014-03-12

Используя новые общие лямбда-выражения, вы можете сделать это:

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) {

    auto pred = [] (auto a, auto b)
                   { return a.first == b.first; };

    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
}

В зависимости от стиля вы также можете встроить лямбда-выражения в C++ 11 и C++ 14 непосредственно в качестве параметра:

bool key_compare (Map const &lhs, Map const &rhs) {
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), 
                      [] (auto a, auto b) { return a.first == b.first; });
}