user997112
user997112

Reputation: 30615

Simplest method to check whether unordered_map of unordered_maps contains key

I am using an unordered_map of unordered_maps, such that I can reference an element using the "multi key" syntax:

my_map[k1][k2].

Is there a convenient way to use the same "multi-key" syntax to check whether an element exists before trying to access it? If not, what is the simplest way?

Upvotes: 41

Views: 89773

Answers (7)

Hui Liu
Hui Liu

Reputation: 407

You might also use count (http://www.cplusplus.com/reference/unordered_map/unordered_map/count/ )

which will return 0 if key not exist

Upvotes: 9

prehistoricpenguin
prehistoricpenguin

Reputation: 6326

An alternative approach is to use std::pair as key to transforming the two-level hashtable into one level hashtable, the benefit:

  • Simpler code and structure
  • Maybe faster than two-level hash table(We call fewer hash functions, get more compacted memory layout to be more cache-friendly)

The drawback: We have some key redundancy, so it would be a bad choice for large keys with many duplications, but this scenario won't be too common so the strategy here is still useful.

std::unordered_map<std::pair<int, int>, int> map;

Then to check exists:

With find and compare with end iterator

map.find(std::make_pair(k0, k1)) != map.end()

With the count function(Be aware that don't use it with unordered_multimap)

map.count(std::make_pair(k0, k1)) != 0

or C++20 contains:

map.contains(std::make_pair(k0, k1))

Upvotes: 0

pooya13
pooya13

Reputation: 2681

In C++20, you can use the contains method (added to all associative containers if I am not mistaken):

if (my_map.contains(k1) && my_map[k1].contains(k2))
{
    // do something with my_map[k1][k2]
}

Upvotes: 17

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275370

template<class M>
bool contains(M const&){return true;}
template<class M, class K, class...Ks>
bool contains(M const&m, K const&k, Ks const&...ks){
  auto it=m.find(k);
  if (it==m.end()) return false;
  return contains(it->second, ks...);
}

will work for every single-valued associative container.

contains(my_map, k1, k2) is true if there is an element k1 which contains k2.

Upvotes: 5

Cory Kramer
Cory Kramer

Reputation: 117856

If your intention is to test for the existence of the key, I would not use

my_map[k1][k2]

because operator[] will default construct a new value for that key if it does not already exist.

Rather I would prefer to use std::unordered_map::find. So if you are certain the first key exists, but not the second you could do

if (my_map[k1].find(k2) != my_map[k1].end())
{
    // k2 exists in unordered_map for key k1
}

If you would like to make a function that checks for the existence of both keys, then you could write something like

//------------------------------------------------------------------------------
/// \brief Determines a nested map contains two keys (the outer containing the inner)
/// \param[in] data Outer-most map
/// \param[in] a    Key used to find the inner map
/// \param[in] b    Key used to find the value within the inner map
/// \return True if both keys exist, false otherwise
//------------------------------------------------------------------------------
template <class key_t, class value_t>
bool nested_key_exists(std::unordered_map<key_t, std::unordered_map<key_t, value_t>> const& data, key_t const a, key_t const b)
{
    auto itInner = data.find(a);
    if (itInner != data.end())
    {
        return itInner->second.find(b) != itInner->second.end();
    }
    return false;
}

Upvotes: 54

Richard Hodges
Richard Hodges

Reputation: 69864

Something like this? (for the mutable case)

using inner_map = std::map<key_type, value_type>;
using outer_map = std::map<key_type, inner_map>

boost::optional<value_type&> 
element_for_keys(outer_map& map, const key_type& k1, const key_type& k2)
{
  auto it_outer = map.find(k1);
  if (it_outer = map.end())
    return {};
  auto &map2 = it_outer->second;
  auto it_inner = map2.find(k2);
  if (it_inner == map2.end())
    return {};

  return { it_inner->second };
}

called like so:

auto op_value = element_for_keys(my_map, kv1, kv2);
if (op_value) {
  // use op_value.value()
}
else {
  // handle case where it does not exist
}

... or there's the more python-like way...

try {
  auto& v = my_map.at(k1).at(k2);
  // use v
}
catch(const std::out_of_range & e) {
  // didn't find it
}

Upvotes: 1

Aiden Deom
Aiden Deom

Reputation: 916

I don't believe there is a multi-key syntax to check, but the simplest way would be to use the find method. You could write a simple function to apply it to a unordered_map of unordered_maps

reference

Upvotes: 0

Related Questions