从包含 std::map 个元素的 std::vector 中查找并删除一个值
Finding and erasing a value from a std::vector holding std::map elements
首先,我有以下两个对象,都填充了数据:
std::vector<std::map<std::uint8_t, std::uint8_t>> x1;
std::vector<std::map<std::uint8_t, std::uint8_t>> x2;
我的 objective 是在 x2
中搜索(通过 键 ),检查 x1
中的任何值是否不存在在 x2
内,然后从 x1
.
中删除它
我尝试了以下代码片段,但无济于事(无法编译!):
for (auto i = x1.begin(); i != x1.end(); ++i)
{
auto it = std::find(x2.begin(), x2.end(), i);
if (it == x2.end())
{
x1.erase(i);
}
}
我做错了什么?您能否分享一些有关如何解决此问题的见解?
你的代码有几个问题:
std::find()
搜索单个匹配元素,在这种情况下意味着您必须给它一个 std::map
来搜索。但是您传递的是 i
迭代器本身,而不是它所引用的 std::map
。您需要取消引用 i
,例如:
auto it = std::find(x2.cbegin(), x2.cend(), *i);
当调用 x1.erase(i)
时,i
失效,这意味着循环不能再使用 i
- 不适用于 ++i
,不适用于 i != x1.end()
。你需要保存erase()
returns的新迭代器,它指的是被擦除后的next元素。这意味着您还需要更新循环逻辑以在调用 erase()
时不增加 i
,例如:
for (auto i = x1.cbegin(); i != x1.cend(); )
{
auto it = std::find(x2.cbegin(), x2.cend(), *i);
if (it == x2.cend())
i = x1.erase(i);
else
++i;
}
最后,当使用 std::find()
时,您是在将整个 std::map
对象相互比较。如果您只想比较 键 ,请尝试更像这样的东西:
for (auto i = x1.cbegin(); i != x1.cend(); )
{
const auto &m1 = *i:
auto it = std::find_if(m1.cbegin(), m1.cend(),
[&](const decltype(m1)::value_type &m1_pair) { // or (const auto &m1_pair) in C++14...
return std::find_if(x2.cbegin(), x2.cend(),
[&](const decltype(x2)::value_type &m2){ // or (const auto &m2) in C++14...
return m2.find(m1_pair.first) != m2.cend();
}
);
}
);
if (it == m1.cend())
i = x1.erase(i);
else
++i;
}
您还可以使用一些功能:Playground
#include <algorithm>
#include <functional>
// removes maps from x1, that are equal to none of x2 maps
auto remove_start = std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(std::equal_to(), x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());
编辑:要仅检查密钥,请将 std::equal_to 更改为自定义 lambda
auto keys_equal = [](auto& m1, auto& m2){
return m1.size() == m2.size()
&& std::equal(m1.begin(), m1.end(), m2.begin(),
[](auto& kv1, auto& kv2){ return kv1.first == kv2.first; });
};
// removes maps from x1, that are equal to none of x2 maps
auto remove_start =
std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(keys_equal, x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());
首先,我有以下两个对象,都填充了数据:
std::vector<std::map<std::uint8_t, std::uint8_t>> x1;
std::vector<std::map<std::uint8_t, std::uint8_t>> x2;
我的 objective 是在 x2
中搜索(通过 键 ),检查 x1
中的任何值是否不存在在 x2
内,然后从 x1
.
我尝试了以下代码片段,但无济于事(无法编译!):
for (auto i = x1.begin(); i != x1.end(); ++i)
{
auto it = std::find(x2.begin(), x2.end(), i);
if (it == x2.end())
{
x1.erase(i);
}
}
我做错了什么?您能否分享一些有关如何解决此问题的见解?
你的代码有几个问题:
std::find()
搜索单个匹配元素,在这种情况下意味着您必须给它一个std::map
来搜索。但是您传递的是i
迭代器本身,而不是它所引用的std::map
。您需要取消引用i
,例如:auto it = std::find(x2.cbegin(), x2.cend(), *i);
当调用
x1.erase(i)
时,i
失效,这意味着循环不能再使用i
- 不适用于++i
,不适用于i != x1.end()
。你需要保存erase()
returns的新迭代器,它指的是被擦除后的next元素。这意味着您还需要更新循环逻辑以在调用erase()
时不增加i
,例如:for (auto i = x1.cbegin(); i != x1.cend(); ) { auto it = std::find(x2.cbegin(), x2.cend(), *i); if (it == x2.cend()) i = x1.erase(i); else ++i; }
最后,当使用
std::find()
时,您是在将整个std::map
对象相互比较。如果您只想比较 键 ,请尝试更像这样的东西:for (auto i = x1.cbegin(); i != x1.cend(); ) { const auto &m1 = *i: auto it = std::find_if(m1.cbegin(), m1.cend(), [&](const decltype(m1)::value_type &m1_pair) { // or (const auto &m1_pair) in C++14... return std::find_if(x2.cbegin(), x2.cend(), [&](const decltype(x2)::value_type &m2){ // or (const auto &m2) in C++14... return m2.find(m1_pair.first) != m2.cend(); } ); } ); if (it == m1.cend()) i = x1.erase(i); else ++i; }
您还可以使用一些功能:Playground
#include <algorithm>
#include <functional>
// removes maps from x1, that are equal to none of x2 maps
auto remove_start = std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(std::equal_to(), x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());
编辑:要仅检查密钥,请将 std::equal_to 更改为自定义 lambda
auto keys_equal = [](auto& m1, auto& m2){
return m1.size() == m2.size()
&& std::equal(m1.begin(), m1.end(), m2.begin(),
[](auto& kv1, auto& kv2){ return kv1.first == kv2.first; });
};
// removes maps from x1, that are equal to none of x2 maps
auto remove_start =
std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(keys_equal, x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());