Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding all keys that correspond to same value in std::unordered_map

Tags:

c++

c++11

stl

c++14

I have an unordered_map looking like this:

std::unordered_map<int, std::string> theMap2 = {{1,"a"}, {2,"b"}, {3,"c"}, {4,"a"}};

I want to find all the keys that have same value lets say "a". Any suggestions beside the obvious way:

std::vector<int> arrKeys;
std::string value = "a";
for (const auto& element : theMap)
   if (element.second == value)
        arrKeys.push_back(element.first); 
like image 690
Dimitar Mirchev Avatar asked Jun 17 '15 12:06

Dimitar Mirchev


2 Answers

I think the "obvious" way is excellent: it is simple, short and easy to read.

Another option is to use stl algorithms. What you need is transform_if algorithm so you can say:

std::transform_if(std::begin(theMap), std::end(theMap),  std::back_inserter(arrKeys), check_value, get_value);

but there is no such in stl. What I can suggest is:

std::vector<int> arrKeys;
std::string value = "a";

auto check_value = [&](std::pair<const int, std::string> const& p)->bool
{
    return (p.second == value);
};

auto end = std::end(theMap);
auto it = find_if(std::begin(theMap), end, check_value);
while (it != end)
{
    arrKeys.push_back(it->first);
    it = find_if(std::next(it), end, check_value);
}
like image 184
Aahzbg Avatar answered Nov 08 '22 23:11

Aahzbg


More out of interest than anything else, I wrote a basic wrapper which could be used for copy_if. It is like std::back_inserter, but instead of just inserting the object it is given, it inserts std::get<N>(obj). This makes it usable for std::array, std::tuple and std::pair.

To insert the pair.first, as we want in this example, we use back_n_inserter<0>.

template< std::size_t N, class Container >
class back_n_insert_iterator : public std::iterator< std::output_iterator_tag,
                                                         void, void, void, void >
{
public:
    back_n_insert_iterator (Container& c) : c{c} {};

    //could protect this by checking that the get call is_convertible to the value_type
    template <typename T>
    decltype(auto) operator= (T&& collec) 
    {
        c.push_back(std::get<N>(std::forward<T>(collec)));
        return *this;
    }

    decltype(auto) operator* () { return *this; }
    decltype(auto) operator++ () { return *this; }
    decltype(auto) operator++ (int) { return *this; }
private:
    Container& c;
};

template< std::size_t N, class Container >
back_n_insert_iterator<N,Container> back_n_inserter( Container& c )
{
    return {c};
}

Now we can use our adapter in a copy_if call:

std::copy_if(theMap.begin(), theMap.end(), 
             back_n_inserter<0>(arrKeys),
             [&value](const auto& el){return el.second == value;});

Thanks to @dyp for all suggestions.

like image 36
TartanLlama Avatar answered Nov 08 '22 23:11

TartanLlama