3

I have a std::unordered_multimap and I want to iterate over each key exactly once.

What I'm currently doing is copying all keys into a std::set. This seems rather inefficient to me and I'm wondering if there is a smarter way of doing this. If it were a std::multiset I would use the std::multiset::upper_bound() member to access the next key, but this member is obviously not available in the unordered version.

I have found a few somewhat related questions and answers but they seem outdated/overcomplicated for my purpose.

So is there a good way of iterating through the different keys? I'm still learning so pointing me in the right direction would also be very much appreciated! Thanks.

pekkas
  • 31
  • 5
  • By iterating over each key, you mean **in order**, don't you? Otherwise, you can directly iterate over the `std::unordered_multimap`. – JFMR Jan 04 '20 at 09:56
  • You can easily keep track of duplicate keys with `std::unordered_set` parametrized for the key: for each iteration, you check whether the key is already in the `std::unordered_set`. If so, you skip the current element. Otherwise, you process this element and insert this element's key into the `std::unordered_set`. However, if the traversal of the keys have to be in order, you need a different approach. – JFMR Jan 04 '20 at 10:04
  • Sorry, English is not my first language what do you mean with '`std::unordered_set` parameterized for the key'? I can't find anything about it on Google. Does this mean that you add each key to the `std::unordered_set`? That would make sense but still seems inefficient. – pekkas Jan 04 '20 at 10:09
  • 2
    Does it really need to be in order? Given the implementation hints in the [c++ reference](https://en.cppreference.com/w/cpp/container/unordered_multimap) I do not see a way to iterate over the keys on order except for your suggestion. Another options is as suggested to iterate over all keys in random order and store information what key you already saw in an unordered_set. For each iteration you check if they key is present in the set. If not do your computation and add it to the set. Otherwise skip the key. – Max Linke Jan 04 '20 at 10:16
  • @MaxLinke Well, I'm learning so I wanted to be in order just for learning purposes. What would you do when the order doesn't matter? – pekkas Jan 04 '20 at 10:25
  • @pekkas I meant, `std::unordered_set` where `key_t` is the type of your key. – JFMR Jan 04 '20 at 10:40
  • @pekkas Both inserting into an `std::unordered_set` as well as looking up can be done in constant time on average. – JFMR Jan 04 '20 at 10:43
  • 2
    why are you using unordered if you need the order – Yamahari Jan 04 '20 at 12:12

1 Answers1

2

[...] elements with equivalent keys are adjacent to each other in the iteration order of the container.

Source, or you can consider that this is guaranteed by the existence of equal_range. Note that "equivalent" is under KeyEqual i.e. it means == if the default std::equal_to<Key> is specified.

So iteration by element, skipping elements with equal keys, will work:

for (auto it = c.begin(); it != c.end(); ) {
    auto const& key = it->first;
    std::cout << key << std::endl;
    while (++it != c.end() && it->first == key) // or c.key_eq()
        ;
}
ecatmur
  • 152,476
  • 27
  • 293
  • 366