Как легко проверить, содержит ли std:: map и std:: unordered_map те же элементы

Я пишу тест Google unit test, и я хочу проверить, совпадает ли содержимое unordered_map<std::string, std::string> с std::map<std::string, std::string>

Я не думаю, что std::equal будет работать, поскольку элементы в std::map сортируются по критерию. Порядок не важен.

Ответ 1

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

Например:

template<typename K, typename E>
bool maps_equal(const std::map<K, E> &map, const std::unordered_map<K, E> &unordered_map) {
    return
        map.size() == unordered_map.size() &&
        std::all_of(map.cbegin(), map.cend(), [&](const std::pair<const K, E> &item) {
            auto iter = unordered_map.find(item.first);
            return iter != unordered_map.end() && iter->second == item.second;
        });
}

Ответ 2

Вы можете создать unordered_map с помощью map, а затем сравнить два unordered_map. И наоборот.

std::unordered_map<std::string, std::string> m1;
std::map<std::string, std::string> m2;
std::unordered_map<std::string, std::string> m3(m2.begin(), m2.end());
if (m1 == m3) {}

Ответ 3

Я задам очевидный вопрос, но он действительно меняет все:

Является ли понятие равенства в map совместимым с понятием равенства в unordered_map?

В качестве примера несовместимых определений:

struct Point3D { std::int32_t x, y, z };

struct MapLess {
    bool operator()(Point3D const& left, Point3D const& right) const {
       return std::tie(left.x, left.y) < std::tie(right.x, right.y);
    }
};

bool operator==(Point3D const& left, Point3D const& right) {
    return std::tie( left.x,  left.z)
        == std::tie(right.x, right.z);
}

В этом (надуманном) случае мы могли бы:

  • map: (1, 2, 3) и (1, 3, 3)
  • unordered_map: (1, 2, 3) и (1, 2, 4)

и наивный поиск сообщит, что map включен в unordered_map, который, поскольку оба они имеют одинаковый размер, приведут к ошибочному выводу, что они равны.


Решение, если существует каноническое понятие равенства, состоит в том, чтобы проверить после каждого поиска, что результат поиска фактически совпадает с исходным.

template <typename M1, typename M2>
bool equal(M1 const& left, M2 const& right) {
    if (left.size() != right.size()) { return false; }

    for (auto const& e: left) {
        auto const it = right.find(e.first);

        if (it == right.end()) { return false; }
        if (it->first != e.first) { return false; }
        if (it->second != e.second) { return false; }
    }

    return true;
}

Примечание: это можно было бы переписать с помощью std::all и одного булевского выражения; это вопрос вкуса, я предпочитаю разбить его.

Если каноническое понятие равенства не существует, то обратный поиск может заменить проверку равенства:

template <typename M1, typename M2>
bool equal(M1 const& left, M2 const& right) {
    if (left.size() != right.size()) { return false; }

    for (auto e = left.begin(), end = left.end(); e != end; ++e) {
        auto const it = right.find(e->first);

        if (it != right.end()) { return false; }
        if (left.find(it->first) != e) { return false; }
        if (it->second != e->second) { return false; }
    }

    return true;
}

Это, конечно, немного дороже.