I am trying to create a map from another map using a comparator function that the new value in the key value pair is not same as the previous value in the key value pair stored in the map.
I am getting a compilation error while compiling below code. What is the issue is with that code? Is there a better way to accomplish this as well?
#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <functional>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
// Declaring the type of Predicate that accepts 2 pairs and return a bool
typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
// Defining a lambda function to compare two pairs. It will compare two pairs using second field
Comparator compFunctor =
[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
{
return elem1.second != elem2.second;
};
// Declaring a set that will store the pairs using above comparision logic
std::map<std::string, int, Comparator> setOfWords(
mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
return 0;
}
The expected output of the second map is:
{ "aaa", 10 }
{ "ddd", 41 }
{ "bbb", 62 }
This means, that { "ccc", 10 } has to be ignored.
Excerpt from the error:
sortMap.cpp:25:70: required from here
/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/stl_tree.h:1422:8:
error: no match for call to
‘(std::function, int>,
std::pair, int>)>) (const
std::basic_string&, const key_type&)’
&& _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
^ In file included from /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/stl_algo.h:66:0,
from /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/algorithm:62,
from sortMap.cpp:4: /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:2174:11:
note: candidate is:
class function<_Res(_ArgTypes...)>
^ /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:2466:5:
note: _Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes
...) const [with _Res = bool; _ArgTypes =
{std::pair,
std::allocator >, int>, std::pair, std::allocator >, int>}]
function<_Res(_ArgTypes...)>::
^
This is a solution according to the intention described by OP.
Sample code:
#include <iostream>
#include <map>
#include <set>
#include <vector>
int main()
{
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = {
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
};
// auxiliary set of values
std::set<int> counts;
// creating a filtered map
std::vector<std::pair<std::string, int> > mapOfWordCountFiltered;
for (const std::map<std::string, int>::value_type &entry : mapOfWordCount) {
if (!counts.insert(entry.second).second) continue; // skip duplicate counts
mapOfWordCountFiltered.push_back(entry);
}
// output
for (const std::pair<std::string, int> &entry : mapOfWordCountFiltered) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
There is no custom predicate used as the standard predicate (std::less<Key>) is sufficient for the solution (for map as well as set).
The filtered map doesn't even use a std::map as there is no necessity for this. (The entries are already sorted, the filtering is done by an extra std::set<int>.)
Actually, I have no idea how to perform this with a custom predicate as I don't know how to keep the (required) order of map with the extra check for duplicated values.
Isn't there a way to create a comparator that makes sure that another "key, value" is not inserted, if the value is already present in the map previously corresponding to a different key? This would save extra space that I would use by creating another set.
I have thought about this a while. Yes, it is possible but I wouldn't recommend it for productive code.
std::map::insert() probably calls std::map::lower_bound() to find the insertion point (i.e. iterator). (The std::map::lower_bound() in turn will use our custom predicate.) If the returned iterator is end() the entry is inserted at end. Otherwise, the key at this iterator is compared with the one which is provided as new (to be inserted). If it is equal the insertion will be denied otherwise the new entry is inserted there.
So, to deny insertion of an entry with duplicated value, the predicate has to return false regardless of comparison of keys. For this, the predicate has to do extra checks.
To perform these extra checks, the predicate needs access to the whole map as well as to the value of entry to be inserted. To solve the first issue, the predicate gets a reference to the map where it is used in. For the second issue, I had no better idea as to use a std::set<std::pair<std::string, int> > instead of the original std::map<std::string, int>. As there is already a custom predicate involved, the sorting behavior can be adjusted sufficiently.
So, this is what I got:
#include <iostream>
#include <map>
#include <set>
#include <vector>
typedef std::pair<std::string, int> Entry;
struct CustomLess;
typedef std::set<Entry, CustomLess> Set;
struct CustomLess {
Set &set;
CustomLess(Set &set): set(set) { }
bool operator()(const Entry &entry1, const Entry &entry2) const;
};
bool CustomLess::operator()(
const Entry &entry1, const Entry &entry2) const
{
/* check wether entry1.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry1InSet = false;
for (const Entry &entry : set) {
if ((entry1InSet = entry.first == entry1.first)) break;
}
/* If entry1 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry1InSet) {
for (const Entry &entry : set) {
if (entry.second == entry1.second) return false;
}
}
/* check wether entry2.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry2InSet = false;
for (const Entry &entry : set) {
if ((entry2InSet = entry.first == entry2.first)) break;
}
/* If entry2 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry2InSet) {
for (const Entry &entry : set) {
if (entry.second == entry2.second) return false;
}
}
/* fall back to regular behavior of a less predicate
* for entry1.first and entry2.first
*/
return entry1.first < entry2.first;
}
int main()
{
// Creating & Initializing a map of String & Ints
// with very specific behavior
Set mapOfWordCount({
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
},
CustomLess(mapOfWordCount));
// output
for (const Entry &entry : mapOfWordCount) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
My collaborator would call this a Frankenstein solution and IMHO this is sufficient in this case.
The intention of a std::map/std::set is usually an amortized insert() and find(). This effect is probably totally lost as the CustomLess must iterate (in worst case) over the whole set twice before a value can be returned. (The possible early-outs from iterations in some cases don't help much.)
So, this was a nice puzzle and I solved it somehow but rather to present a counter example.
As #Galik mentioned in the comments, the problem with your code is that the compare function of a map expects two keys as parameters and not key-value pairs. Consequently, you don't have access to the values within the comparator.
Similar to #Scheff, I also don't see a way to make your solution using a custom comparator work in a practical or recommended way. But instead of using a set and a vector, you could also invert your map. The filtering can then be performed by the map::insert() function:
#include <map>
#include <string>
#include <iostream>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
std::map<int, std::string> inverseMap;
for(const auto &kv : mapOfWordCount)
inverseMap.insert(make_pair(kv.second, kv.first));
for(const auto& kv : inverseMap)
std::cout << "{ \"" << kv.second << "\", " << kv.first << " }" << std::endl;
}
The function map::insert() only inserts an item if its key doesn't exist in the map, yet. Output:
{ "aaa", 10 }
{ "ddd", 41 }
{ "bbb", 62 }
However, if you require your target map setOfWords to be of the type std::map<std::string, int>, then you can invert the inverted map from the code above once again in the following way:
std::map<std::string, int> setOfWords;
for(const auto& kv : inverseMap)
setOfWords[kv.second] = kv.first;
for(const auto& kv : setOfWords)
std::cout << "{ \"" << kv.first << "\", " << kv.second << " }" << std::endl;
As a result (even if this isn't your requirement), setOfWords becomes sorted by the key. Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Code on Ideone
Related
Let's say I have the following object:
vector<string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
I'm trying to find the first non-repeating entry in the data object.
For example, data.find_first_not_of(data.at(0)); this would work if data is of string type only (no container).
How can I achieve the same thing with an object of type vector.
I looked at adjacent_find and find_if_not from the algorithm library, but to no avail.
Your suggestions are much appreciated.
What problem did you have with adjacent_find? You should be able to use that with an inverse predicate:
std::vector<std::string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
// Sort data here if necessary
auto itr = std::adjacent_find(data.cbegin(), data.cend(), std::not_equal_to<std::string>{});
if (itr != data.cend()) {
std::cout << "First mismatch: " << *itr << " " << *std::next(itr) << std::endl;
} else {
std::cout << "All elements equal" << std::endl;
}
Wandbox
Since you have to go through the list at least once, and you don't know when or where you will encounter the duplicate of a number (if there is one), one way to solve this is to first gather "statistics" and then from what you've gathered you can determine the first non-duplicate.
Here is an example using std::unordered_map:
#include <algorithm>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <string>
// struct to hold some information on the numbers
struct info
{
std::string number;
int count;
int position;
info(const std::string n, int c, int p) : number(n), count(c), position(p) {}
};
int main()
{
std::vector<std::string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
std::unordered_map<std::string, info> infoMap;
std::vector<info> vInfo;
int pos = 0;
// loop for each data element
std::for_each(data.begin(), data.end(), [&](const std::string& n)
{
// insert entry into the map
auto pr = infoMap.insert(std::make_pair(n, info(n, 0, pos)));
// bump up the count for this entry.
++pr.first->second.count;
// bump up the postion number
++pos;
});
// create a vector of the information with a count of 1 item.
std::for_each(infoMap.begin(), infoMap.end(), [&](std::unordered_map<std::string, info>::value_type& vt) { if (vt.second.count == 1) vInfo.push_back(vt.second); });
// sort this by position
std::sort(vInfo.begin(), vInfo.end(), [&](const info& pr1, const info &pr2){return pr1.position < pr2.position; });
// output the results
if ( vInfo.empty() )
std::cout << "All values are duplicated\n";
else
std::cout << "The first number that isn't repeated is " << vInfo.front().number << "\n";
}
Live Example
First, we just simply go through all the entries in the vector and just tally up the count for each item. In addition, we store the position in the original list of where the item was found.
After that we filter out the ones with a count of exactly 1 and copy them to a vector. We then sort this vector based on the position they were found in the original list.
I have a std::map object. Keys are entity IDs (integers) and values their 2D positions (vectors). The aim is to identify, which entities are in the
same position.
ID Position
1 {2,3}
5 {6,2}
12 {2,3}
54 {4,4}
92 {6,2}
I need to get a vector of vectors consisting of keys, which have equal values.
Output for the example input data above: {1,12}, {5,92}
I know I can copy the 2D positions to vector to vectors and loop the first level vector to find indexes of equal second level vectors. Then work back to find keys by selecting the vectors by index and looping again to find the corresponding keys.
Please suggest a cleaner approach for this.
The point of an std::map is to provide an efficient key to value mapping. What you need is an additional value to key mapping - that can be achieved in multiple ways:
Have with an extra std::map that goes from Position to std::vector<ID>.
Use some sort of spatial partitioning data structure (e.g. quadtree, spatial hash, grid) that makes it efficient to find entities depending on their position.
Use a bidirectional multi-map like boost::bimap. This will allow you to have a bidirectional mapping over collection of values without having to use multiple data structures.
"How do I choose?"
It depends on your priorities. If you want maximum performance, you should try all the approaches (maybe using some sort of templatized wrapper) and profile. If you want elegance/cleanliness, boost::bimap seems to be the most appropriate solution.
You could put your data from the map into a std::mutlimap, with the Position as key and ID as value.
As a side note I wonder if a std::pair might be better than a vector for 2d points.
This answer seems to be best, but I'll offer my code anyway.
Given
#include <iostream>
#include <map>
#include <vector>
// Some definiton of Vector2D
struct Vector2D { int x; int y; };
// and some definition of operator< on Vector2D
bool operator<(Vector2D const & a, Vector2D const & b) noexcept {
if (a.x < b.x) return true;
if (a.x > b.x) return false;
return a.y < b.y;
}
How about:
template <typename M>
auto calculate(M const & inputMap) -> std::vector<std::vector<typename M::key_type> > {
std::map<typename M::mapped_type,
std::vector<typename M::key_type> > resultMap;
for (auto const & vp : inputMap)
resultMap[vp.second].push_back(vp.first);
std::vector<std::vector<typename M::key_type> > result;
for (auto & vp: resultMap)
if (vp.second.size() > 1)
result.emplace_back(std::move(vp.second));
return result;
}
Here's how to test:
int main() {
std::map<int, Vector2D> input{
{1, Vector2D{2,3}},
{5, Vector2D{6,2}},
{13, Vector2D{2,3}},
{54, Vector2D{4,4}},
{92, Vector2D{6,2}}
};
auto const result = calculate(input);
// Ugly print
std::cout << '{';
static auto const maybePrintComma =
[](bool & print) {
if (print) {
std::cout << ", ";
} else {
print = true;
}
};
bool comma = false;
for (auto const & v: result) {
maybePrintComma(comma);
std::cout << '{';
bool comma2 = false;
for (auto const & v2: v) {
maybePrintComma(comma2);
std::cout << v2;
}
std::cout << '}';
}
std::cout << '}' << std::endl;
}
You need to provide a reverse mapping. There are a number of ways to do this, including multimap, but a simple approach if your mapping isn't modified after creation is to iterate over the map and build up the reverse mapping. In the reverse mapping, you map value -> list of keys.
The code below uses std::unordered_map to map std::pair<int, int> (the value in the original map) to std::vector<int> (list of keys in the original map). The building of the reverse map is simple and concise:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
(See the full example for the definition of hash).
There's no need to worry about whether the key exists; it will be created (and the vector of ids will be initialised as an empty vector) when you attempt to access that key using the r[key] notation.
This solution targets simplicity; it's a workable solution if you need to do this and don't care about performance, memory usage or using third-party libraries like Boost.
If you do care about any of those things, or you're modifying the map while doing lookups in both directions, you should probably explore other options.
Live example
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
// Define a point type. Use pair<int, int> for simplicity.
using Point = std::pair<int, int>;
// Define a hash function for our point type:
struct hash {
std::size_t operator()(const Point& p) const
{
std::size_t h1 = std::hash<int>{}(p.first);
std::size_t h2 = std::hash<int>{}(p.second);
return h1 ^ (h2 << 1);
}
};
int main() {
// The original forward mapping:
std::map<int, Point> m = {
{1, {2, 3}},
{5, {6, 2}},
{12, {2, 3}},
{54, {4, 4}},
{92, {6, 2}}
};
// Build reverse mapping:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
// DEMO: Show all indices for {6, 2}:
Point val1 = {6, 2};
for (const auto& id : r[val1]) {
std::cout << id << " ";
}
std::cout << "\n";
// DEMO: Show all indices for {2, 3}:
Point val2 = {2, 3};
for (const auto& id : r[val2]) {
std::cout << id << " ";
}
std::cout << "\n";
}
Finding an element in a vector of structures
this link showed me, how to look for a value inside a structure.
but i have something like this,
struct sample {
string name;
vector<string> values;
};
vector<sample>v1;
and this is a vector of structures. how to search for a particular string in the values vector, that is present inside the structure samples ? which itself, is a vector of structures ?
thanks.
You can iterate through the vector v1 containing sample structures accessing each vector v1 member as a struct. Then, you can access the struct member vector to search for desired string:
for (const sample &it : v1) {
for (const string &st : it.values) {
if (st == ...) {
}
}
}
You can use a combination of std::find_if and std::find.
The std::find_if goes through the sample objects and checks every element with a predicate which itself uses std::find to go through all std::string elements inside and compares each of them to the token you want to find.
Here is an example, using a lambda function to create the predicate:
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
struct sample
{
std::string name;
std::vector<std::string> values;
};
int main()
{
std::vector<sample> const v1 =
{
{ "one", { "a", "b" } },
{ "two", { "c", "token to find", "d", "e" } },
{ "three", { "f"} }
};
using std::begin;
using std::end;
auto const token = "token to find";
// go through all samples
auto const sample_iter = std::find_if(begin(v1), end(v1), [&token](sample const& s)
{
// in each sample, go through all values
auto const string_iter = std::find(begin(s.values), end(s.values), token);
return string_iter != end(s.values);
});
if (sample_iter == end(v1))
{
std::cout << "not found\n";
}
else
{
std::cout << sample_iter->name << '\n';
}
}
Output:
two
I want to create unordered_map(Because I specifically want a hash map). I want to allocate its max size (according to my constraints) in the beginning.
So, if I want to allocated 256 entries, and the size of each entry is 1B (just an example. Let's say 1Byte includes the Key and the Value). Then the total size of my unordered_map keys + entries is 256B. I want to pre-allocate 256B in the allocator.
Then, when the unordered_map will call allocate()/deallocate(), the allocator will give it 1B from the already-allocated memory.
typedef boost::unordered::unordered_map<int, MyClass, boost::hash<int>, std::equal_to<MyClass>, ??? > > myMap
Does it exists in BOOST? or somewhere else?
---- edit ----
As I see it (Thanks to the answers here) - there are two solutions for my problem:
Implement an allocator, which holds a boost::pool<>. This pool is built in the allocator constructor. When allocate() is being called from unordered_map, it actually calls pool.malloc(), and when deallocate() is called from unordered_map, it actually calls pool.free().
Use an already implemented allocator, such as pool_allocator like this:
typedef pool_allocator<std::pair<MyKey, MyClass>, boost::default_user_allocator_new_delete, boost::mutex, 1024 >) MyAllocator;
typedef unordered_map<MyKey, MyClass, hash, eq, MyAllocator> MyUnorderedMap;
The seconds option is still unclear to me, because:
a. Can I declare only one MyUnorderedMap?
b. How can I declare a new MyUnorderedMap with different next_block size than 1024 in run time?
What you describe can actually only achieved with something like Boost Intrusive "Maps" (actually, sets then).
However to get truly 1B - allocated elements you'd need to define a custom stateful value traits, so you can store the node-index metadata separately from the element payload.
However, from the fact that you claim the element type to be 1B (which can obviously never be true for a concrete key and value type), I'll not assume you actually wanted this contrived solution for "some reason".
Instead, let me suggest three more mundane approaches:
Using a flat_map
Using a Boost Intrusive unordered set
Using an unordered set with Boost Pool fixed size allocator¹
Boost flat_map
If hash lookup is not mandatory, you can simplify a lot by just reserving contiguous element storage up front and storing an ordered map instead:
Live On Coliru
#include <boost/container/flat_map.hpp>
#include <iostream>
using Elements = boost::container::flat_map<std::string, std::string>;
int main() {
Elements map;
map.reserve(256); // pre-allocate 256 "nodes"!
map.insert({
{ "one", "Eins" },
{ "two", "Zwei" },
{ "three", "Drei" },
{ "four", "Vier" },
{ "five", "Fuenf" },
});
for (auto& e : map) {
std::cout << "Entry: " << e.first << " -> " << e.second << "\n";
}
std::cout << "map[\"three\"] -> " << map["three"] << "\n";
}
Prints
Entry: five -> Fuenf
Entry: four -> Vier
Entry: one -> Eins
Entry: three -> Drei
Entry: two -> Zwei
map["three"] -> Drei
Boost Intrusive
CAVEAT Intrusive containers come with their own set of trade offs. Managing the underlying storage of the elements can be error-prone. Auto-link behaviour of the hooks inhibits the constant-time implementation of size() and similar (empty() on some of the unordered set configurations) so this might not be your thing.
Live On Coliru
#include <boost/intrusive/unordered_set.hpp>
#include <boost/intrusive/unordered_set_hook.hpp>
#include <iostream>
namespace bi = boost::intrusive;
struct Element;
namespace boost {
template <> struct hash<Element> {
size_t operator()(Element const& e) const;
};
}
struct Element : bi::unordered_set_base_hook<> {
std::string key;
mutable std::string value;
Element(std::string k = "", std::string v = "")
: key(std::move(k)), value(std::move(v)) { }
bool operator==(Element const& other) const { return key == other.key; }
};
size_t boost::hash<Element>::operator()(Element const& e) const {
return hash_value(e.key);
}
using Elements = bi::unordered_set<Element>;
int main() {
std::array<Element, 256> storage; // reserved 256 entries
std::array<Elements::bucket_type, 100> buckets; // buckets for the hashtable
Elements hashtable(Elements::bucket_traits(buckets.data(), buckets.size()));
storage[0] = { "one", "Eins" };
storage[1] = { "two", "Zwei" };
storage[2] = { "three", "Drei" };
storage[3] = { "four", "Vier" };
storage[4] = { "five", "Fuenf" };
hashtable.insert(storage.data(), storage.data() + 5);
for (auto& e : hashtable) {
std::cout << "Hash entry: " << e.key << " -> " << e.value << "\n";
}
std::cout << "hashtable[\"three\"] -> " << hashtable.find({"three"})->value << "\n";
}
Prints
Hash entry: two -> Zwei
Hash entry: four -> Vier
Hash entry: five -> Fuenf
Hash entry: three -> Drei
Hash entry: one -> Eins
hashtable["three"] -> Drei
Pool fixed size allocator¹
If you absolutely require the node-based storage, consider using a custom allocator.
¹ You'll note that (at least with Boost's unordered_map implementation) the allocator is used for two types (bucket pointers and value nodes) and as such there are two fixed size allocations possible.
(See the cleanup calls at the bottom of the sample)
Live On Coliru
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered/unordered_map.hpp>
#include <iostream>
using RawMap = boost::unordered_map<std::string, std::string>;
using Elements = boost::unordered_map<
std::string, std::string,
RawMap::hasher, RawMap::key_equal,
boost::fast_pool_allocator<RawMap::value_type>
>;
int main() {
{
Elements hashtable;
hashtable.insert({
{ "one", "Eins" },
{ "two", "Zwei" },
{ "three", "Drei" },
{ "four", "Vier" },
{ "five", "Fuenf" },
});
for (auto& e : hashtable) {
std::cout << "Hash entry: " << e.first << " -> " << e.second << "\n";
}
std::cout << "hashtable[\"three\"] -> " << hashtable.find("three")->second << "\n";
}
// OPTIONALLY: free up system allocations in fixed size pools
// Two sizes, are implementation specific. My 64 system has the following:
boost::singleton_pool<boost::fast_pool_allocator_tag, 8>::release_memory(); // the bucket pointer allocation
boost::singleton_pool<boost::fast_pool_allocator_tag, 32>::release_memory(); // the ptr_node<std::pair<std::string const, std::string> >
}
I want to collect keys of the same value in a map. What is the easiest way to do it using vector? That means all the keys having the same value can be collected in a vector.
You will have to do a linear search over the whole container, which is O(N).
std::vector<Value> values;
std::for_each(map.begin(), map.end(),
[&](std::map<Key,Value>::value_type const & x) {
if (x.second == value)
values.push_back(x.first);
});
If you want to extract all keys for which the value is not unique, the complexity of the code is higher, and you will need additional data, but you could do something like this:
std::map<Value, std::pair<Key, bool>> tracker;
// Maps a 'Value' to the first 'Key' that had it, and a 'bool'
// identifying if it has already been inserted into the vector.
std::vector<Key> keys;
for_each(m.begin(), m.end(),
[](std::map<Key, Value>::value_type const& x) {
auto r = tracker.insert(std::make_pair(x.second,
std::make_pair(x.first, false));
if (!r.second) {
// Not the first time we saw this value
if (!r.first->second) {
// First key not already inserted, insert now and update flag
keys.push_back(r.first);
r.first->second = true;
}
keys.push_back(x.first);
}
});
Although in real code I would avoid using std::pair and would create a named type that makes the code simpler to read. In the code above it is not obvious what all those first and second mean…
A different alternative, probably more efficient (measure and profile) would be to use transform to create a vector where the elements are swapped and then iterate over that vector extracting the values of interest.
You can do it the following way
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main()
{
std::map<int, std::string> m
{
{ 1, "Monday" }, { 2, "Tuesday" }, { 9, "Monday" }
};
std::vector<int> v;
size_t n = 0;
std::string s( "Monday" );
for ( const auto &p : m )
{
if ( p.second == s ) ++n;
}
v.reserve( n );
for ( const auto &p : m )
{
if ( p.second == s ) v.push_back( p.first );
}
for ( const auto &x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output is
1 9
You can substitute the range based for statements for correspondingly std::count_if and std::for_each algorithms along with lambda expressions. But in my opiniion for this simple task it is better to use the range based for statements.
Where you are creating the map, consider creating a unordered_multimap with the key and value of the original map swapped.