如何获取两个 std::map 的公钥?

How could I obtain the common keys of two std::map?

提问人:coin cheung 提问时间:12/26/2019 最后编辑:Evgcoin cheung 更新时间:12/28/2019 访问量:576

问:

假设有两个 c++:std::map

std::map<string, int> m1{{"n1", 1}, {"n2", 2}, {"n3", 3}};
std::map<string, int> m2{{"n2", 3}, {"n4", 2}, {"n1", 9}};

我想获取包含两个映射的公共键。std::set

std::set<string> common = ...;

我希望我能优雅地做到这一点,所以我最好避免使用循环,如果我可以在命名空间中找到一些方法,或者从中找到一些方法会更好,我该怎么做?std<algorithm>

C++ 字典 std

评论

1赞 t.niese 12/26/2019
这回答了你的问题吗?如何找到 2 张地图中常见的所有键?
0赞 t.niese 12/26/2019
从理论上讲,如果您不想直接使用循环,您可以组合例如 a 和 a。但更有效的方法是使用可能的重复项的公认答案。std::transformstd::erase_if
1赞 Jesper Juhl 12/26/2019
使用 std::set_intersection 怎么样?

答:

1赞 parktomatomi 12/26/2019 #1

要查找公共密钥,您需要过滤其密钥不在中的项目,然后提取密钥。可悲的是,中没有或任何类型的组合过滤器/映射操作,所以如果你使用纯算法,这是一个两步操作:m1m2transform_if<algorithm>

std::map<string, int> common_m;
std::copy_if(
    m1.begin(), m1.end(), 
    std::inserter(common_m, common_m.end()), 
    [&m2](auto&& kv) { return m2.find(kv.first) != m2.end(); });

std::set<string> common_keys;
std::transform(
    common_m.begin(), common_m.end(), 
    std::inserter(common_keys, common_keys.end()), 
    [](auto&& kv) { return kv.first; });

演示 - https://godbolt.org/z/6982Pm

编写步骤很笨拙,因为没有惰性操作,每个操作都必须在新容器中具体化。<algorithm>

这就是 ranges-v3 库的功能,该库已作为 Ranges TS 并入 C++20 标准。它允许 ML 风格的惰性操作,这些操作可以组合成一个单行:

std::set<string> common_keys = m1
    | views::remove_if([&m2](auto&& kv) { return m2.find(kv.first) == m2.end(); })
    | views::transform([](auto&& kv) { return kv.first; })
    | to<std::set>;

演示 - https://godbolt.org/z/CKUFFz

3赞 Evg 12/26/2019 #2

使用 std::set_intersection 的可能解决方案:

template<class It>
class Key_iterator {
public:
    Key_iterator(It it) : it_(it) {}

    Key_iterator& operator++() {
        ++it_;
        return *this;
    }

    bool operator==(const Key_iterator& other) const {
        return it_ == other.it_;
    }

    bool operator!=(const Key_iterator& other) const {
        return it_ != other.it_;
    }

    auto operator*() const {
        return it_->first;
    }    

private:
    It it_;
};

std::map<std::string, int> m1{{"n1", 1}, {"n2", 2}, {"n3", 3}};
std::map<std::string, int> m2{{"n2", 3}, {"n4", 2}, {"n1", 9}};

std::vector<std::string> s;
std::set_intersection(Key_iterator(m1.begin()), Key_iterator(m1.end()),
        Key_iterator(m2.begin()), Key_iterator(m2.end()), std::back_inserter(s));

// s = {"n1", "n2"}

std::set_intersection需要对两个范围进行排序,并按排序顺序保留其键。所以它们很合身。std::map


如果 Boost 可用,则无需编写适配器代码。使用 boost::transform_iterator 可以将此代码简化为:Key_iterator

std::map<std::string, int> m1{{"n1", 1}, {"n2", 2}, {"n3", 3}};
std::map<std::string, int> m2{{"n2", 3}, {"n4", 2}, {"n1", 9}};

auto key_iterator = [](auto it) {
    return boost::transform_iterator(it, [](auto& p) { return p.first; });
};

std::vector<std::string> s;
std::set_intersection(key_iterator(m1.begin()), key_iterator(m1.end()),
    key_iterator(m2.begin()), key_iterator(m2.end()), std::back_inserter(s));

评论

0赞 Narase 4/13/2021
真的很喜欢这个Key_iterator解决方案。现在做了很多 map-alg,这很有帮助