dict
objects in Python have an update
method that can be used to extend one dictionary with all items from another, possibly overwriting entries that already exist. I find this function very useful from time to time.
The associative containers from the C++ standard library (std::map
and std::unordered_map
) do not provide a direct analogue for this functionality so I wrote the following utility functions.
Comments regarding any aspect of the code are welcome.
#include <cassert>
#include <utility>
namespace codereview
{
template <typename DictT>
void update(DictT& dst, const DictT& src)
{
for (const auto& [key, value] : src) {
dst.insert_or_assign(key, value);
}
}
template <typename DictT>
void update(DictT& dst, DictT&& src) noexcept
{
// Equal allocators are a precondition for splicing nodes
assert(src.get_allocator() == dst.get_allocator());
dst.merge(src);
while (!src.empty()) {
auto node = src.extract(src.cbegin());
dst.extract(node.key());
dst.insert(std::move(node));
}
}
} // namespace codereview
-
1\$\begingroup\$ I asked about the weird interface on SO. Feel free to join in guys, this looks really interesting. Might as well be a bug. \$\endgroup\$Incomputable– Incomputable2017年11月23日 20:56:34 +00:00Commented Nov 23, 2017 at 20:56
1 Answer 1
You're not thinking laterally here. If you std::swap()
your two inputs, then you can merge()
the original into the new, and the contents of the new map will prevail.
Here's what I mean:
namespace codereview
{
template <typename DictT>
void update(DictT& dst, DictT src)
{
std::swap(dst, src);
dst.merge(src);
}
} // namespace codereview
// Test
#include <iostream>
#include <map>
template<typename Map>
void print(const Map& x)
{
for (auto const& i: x)
std::cout << i.first << "=" << i.second << " ";
std::cout << std::endl;
}
int main()
{
using Map = std::map<int, const char*>;
Map a { {0, "zero"}, {1, "one"} };
const Map b { {0, "nothing"}, {2, "two"}};
print(a);
// test lvalue argument
codereview::update(a, b);
print(a);
// test rvalue argument
codereview::update(a, Map{{3, "three"}, {0, "nada"}});
print(a);
}
Output
0=zero 1=one
0=nothing 1=one 2=two
0=nada 1=one 2=two 3=three
Note that I changed the method signature to accept src
by copy - the compiler will move-construct into src
if it can, but if not, we'll need a copy so that we don't perturb the caller's version when we swap.
-
\$\begingroup\$ I see now. Really good solution. I kind of ignored effects of swap \$\endgroup\$Incomputable– Incomputable2017年11月23日 18:00:27 +00:00Commented Nov 23, 2017 at 18:00
-
\$\begingroup\$ Nice! This solution is indeed more elegant and probably also more efficient than mine. \$\endgroup\$5gon12eder– 5gon12eder2017年11月24日 14:07:39 +00:00Commented Nov 24, 2017 at 14:07