Values in unordered_map are changing itself - c++

Having trouble with unordered_map named visited.
The goal of visited is to mark visited vertices in graph which are pairs of char and int.
The problem is that values in visited change itself even if I changed them manually after the initialization of visited.
I tried simpler method to declare hash for pair of int and char but it didn't work.
Then I tried to change pair of char and int to int, by multiplicating char by 1M and adding int.
However it didn't work either.
Details:
When function BFS is called again then the same key (i.e. key is pair<'c', 1> converted to int)
is called in condition:
if ( visited[(pair.first - '0')*1000000 + pair.second] == 0 )
then the condition is passed even if I assign to this key a value of 1 earlier.
#include <vector>
#include <unordered_map>
#include <list>
#include <utility>
using namespace std;
typedef std::pair<char, int> p;
// define hash for pair<char, int>
struct pair_hash
{
template <class T1, class T2>
std::size_t operator() (const std::pair<T1, T2> &p) const
{
return std::hash<T1>()(p.first) ^ std::hash<T2>()(p.second);
}
};
void BFS(pair<char, int> v,
unordered_map<int, int> visited,
unordered_map<pair<char, int>, vector<pair<char, int>>, pair_hash> graph) {
// Maybe int instead of a pair<char, int> as a key will work...
int x = (v.first - '0')*1000000 + v.second;
if (visited[x] == 0) {
// Create a queue for BFS
list<pair<char, int>> queue;
// Mark the current node as visited and enqueue it
visited[x] = 1;
queue.push_back(v);
while(!queue.empty()) {
// Dequeue a vertex from queue
v = queue.front();
queue.pop_front();
// Get all adjacent vertices of the dequeued
// vertex s. If a adjacent has not been visited,
// then mark it visited and enqueue it
for (p P : graph[v]) {
int y = (P.first - '0')*1000000 + P.second;
// Problem lays here
if (visited[y] == 0) {
// now I set converted pair<'c', 1> to int to 1:
visited[y] = 1;
// After 1, 2 calls of function named BFS the condtion
// if ( visited[ converted pair<'c', 1> to int ] == 0 )
// is passed even if I set it to 1 earlier
queue.push_back(P);
}
}
}
}
}
int main() {
unordered_map<pair<char, int>, vector<pair<char, int>>, pair_hash> graph;
// Container for marking vertices as visited
unordered_map<int, int> visited;
for (auto kv: graph) {
int x = (kv.first.first - '0')*1000000 + kv.first.second;
visited[x] = 0;
}
// Graph traversal
for (auto kv: graph) {
BFS(kv.first, visited, graph);
}
}
return 0;
}

Related

Initialising a 2d map c++

I have been thinking lately about 2d structures (of integers) in which their sizes can grow dynamically. I came across 'map' which I think that it meets my following need: Essentially, what I want is to check whether a random entry has been initialized or not (do something if it has not been initialized yet).
int main(){
int int1, int2;
std::map<int,std::map<int,int>> my_map;
cout << "Enter two integers separated by space: ";
cin >> int1 >> int2;
if(has my_map[int1][int2] not been initialized){
do something
}
}
I am hoping that such functionality is available with C++.
If what you mean by checking whether an entry "has been initialized" is checking that a pair of keys has a value assigned to them -- one key for the outer map and one for the inner map -- you can test contents of a map of maps as below:
bool contains_keys(const std::map<int, std::map<int, int>>& map_of_maps, int key1, int key2) {
auto iter = map_of_maps.find(key1);
if (iter == map_of_maps.end()) {
return false;
}
return iter->second.find(key2) != iter->second.end();
}
However, I question whether a map of maps is really what you want. If what you want is just a mapping from two keys to a single value, a more direct and space efficient implementation is to use an std::map with an std::tuple or std::pair as the key type.
Tuples version below.
#include <map>
#include <tuple>
#include <iostream>
int main()
{
std::map<std::tuple<int, int>, int> map_of_pairs;
map_of_pairs[{42, 17}] = 3;
bool contains_42_17 = (map_of_pairs.find({ 42, 17 }) != map_of_pairs.end());
bool contains_67_23 = (map_of_pairs.find({ 67, 23 }) != map_of_pairs.end());
std::cout << ((contains_42_17) ? "yes\n" : "no\n");
std::cout << ((contains_67_23) ? "yes\n" : "no\n");
}
Also unless you actually need the above to be ordered, consider an unordered_map.
You could write a function that first checks the outer map for key1, then if an inner map exists with that key, check the inner map for key2.
bool nested_contains(std::map<int,std::map<int,int>> const& my_map, int key1, int key2)
{
auto itOuter = my_map.find(key1);
if (itOuter == my_map.end())
return false;
return itOuter->second.contains(key2);
}
Then use it like
int main()
{
int int1, int2;
std::map<int,std::map<int,int>> my_map;
std::cout << "Enter two integers separated by space: ";
std::cin >> int1 >> int2;
if(nested_contains(my_map, int1, int2)){
// do something
}
}
You can quickly do it creating a custom function using the find method.
In that case the algorithm is suitable for being generic.
template<typename T>
bool contains_keys_cpp_20(std::map<T, std::map<T, T>> const& nested_map, T key1, T key2)
{
auto pair = nested_map.find(key1);
return (pair != nested_map.end()) && (pair->second.contains(key2));
}
template<typename T>
bool contains_keys_cpp_17(std::map<T, std::map<T, T>> const& nested_map, T key1, T key2)
{
auto pair = nested_map.find(key1);
return (pair != nested_map.end()) && (pair->second.find(key2) != pair->second.end());
}
int main()
{
{
std::map<int, std::map<int, int>> my_map;
my_map[1] = { { 2, 1} };
bool result = contains_keys_cpp_17(my_map, 3, 2);
// false
}
{
std::map<int, std::map<int, int>> my_map;
my_map[3] = { { 2, 1} };
bool result = contains_keys_cpp_17(my_map, 3, 2);
// true
}
{
std::map<char, std::map<char, char>> my_map;
my_map['3'] = { { '2', '1'} };
bool result = contains_keys_cpp_17(my_map, '3', '2');
// true
}
return 0;
}

Is there an efficient algorithm for merging numeric ranges?

I am given series of ranges and I need to iterate each number in any of the ranges exactly once. The ranges may overlap and contain the same numbers.
The numbers in the range are
using Number = uint32_t;
Ranges are of this form
struct Range {
Number first;
Number last;
Number interval;
};
Just to clarify the representation of Range.
Range range = {
2, //first
14, //last
3 //interval
};
//is equivalent to...
std::vector<Number> list = {2, 5, 8, 11, 14};
I have a few Ranges and I need to efficiently iterate all of the numbers in any order only once.
How do I efficiently iterate a set of ranges?
Also, Is there there a more efficient algorithm if interval is always 1?
For each range, remember the "current" value (going from first to last with the step size). Put that along with the range in a priority queue, sorted after the current value.
Take the top out, if its current value is different from the last, then use it. Then, insert the next step if there is another.
Assumes positive step size.
template<typename Iterator, typename Operation>
void iterate_ranges (Iterator from, Iterator to, Operation op) {
using R = typename std::iterator_traits<Iterator>::value_type;
using N = typename std::decay<decltype(std::declval<R>().first)>::type;
using P = std::pair<N, R>;
auto compare = [](P const & left, P const & right) {
return left.first > right.first;};
std::priority_queue<P, std::vector<P>, decltype(compare)> queue(compare);
auto push = [& queue] (P p) {
if (p.first < p.second.last) queue.push(p); };
auto next = [](P const & p) -> P {
assert(p.second.step > 0);
return {p.first + p.second.step, p.second}; };
auto init = [&push] (R const & r) {
push({r.first, r}); };
std::for_each(from, to, init);
if (queue.empty()) return;
N last = queue.top().first;
push(next(queue.top()));
queue.pop();
op(last);
while (! queue.empty()) {
P current = queue.top();
queue.pop();
if (current.first != last) {
op(current.first);
last = current.first;
}
push(next(current));
}
}
Memory requirement: linear in the number of ranges. Time requirement: sum of all step counts within each range.
Small example:
struct Range {
int first;
int last;
int step; // a better name ...
};
int main() {
Range ranges [] = {
{1, 10, 2},
{2, 50, 5}};
auto print = [](auto n) { std::cout << n << std::endl; };
iterate_ranges(std::begin(ranges), std::end(ranges), print);
}
To get all numbers in a vector, use a lambda with a reference to a vector and push back each one.
Is there there a more efficient algorithm if interval is always 1?
You could add that as a special case, but I don't think it will be necessary. If you only got ~50 ranges, then above push won't be that expensive. Though, with all optimisation: profile first!
If the sequences are very long you might like to just take each result in order, without storing the list, discarding duplicates as you go.
#include <vector>
// algorithm to interpolate integer ranges/arithmetic_sequences
template<typename ASqs, typename Action>
void arithmetic_sequence_union(ASqs arithmetic_sequences, Action action)
{
using ASq = ASqs::value_type;
using T = ASq::value_type;
std::vector<ASq> remaining_asqs(begin(arithmetic_sequences), end(arithmetic_sequences));
while (remaining_asqs.size()) {
// get next value
T current_value = **std::min_element(begin(remaining_asqs), end(remaining_asqs),
[](auto seq1, auto seq2) { return *seq1 < *seq2; }
);
// walk past this value and any duplicates, dropping any completed arithmetic_sequence iterators
for (size_t seq_index = 0; seq_index < remaining_asqs.size(); )
{
ASq &asq = remaining_asqs[seq_index];
if (current_value == *asq // do we have the next value in this sequence?
&& !++asq) { // consume it; was it the last value in this sequence?
remaining_asqs.erase(begin(remaining_asqs) + seq_index);//drop the empty sequence
}
else {
++seq_index;
}
}
action(current_value);
}
}
This wants the range presented in a "generator"-type object. Would probably look very like the implementation of checked a iterator, but iterators don't expose the notion of knowing they are at the end of the sequence so we might have to roll our own simple generator.
template <typename ValueType, typename DifferenceType>
class arithmetic_sequence {
public:
using value_type = ValueType;
using difference_type = DifferenceType;
arithmetic_sequence(value_type start, difference_type stride, value_type size) :
start_(start), stride_(stride), size_(size) {}
arithmetic_sequence() = default;
operator bool() { return size_ > 0; }
value_type operator*() const { return start_; }
arithmetic_sequence &operator++() { --size_; start_ += stride_; return *this;}
private:
value_type start_;
difference_type stride_;
value_type size_;
};
Test example:
#include "sequence_union.h"
#include "arithmetic_sequence.h"
#include <cstddef>
#include <array>
#include <algorithm>
#include <iostream>
using Number = uint32_t;
struct Range {
Number first;
Number last;
Number interval;
};
using Range_seq = arithmetic_sequence<Number, Number>;
Range_seq range2seq(Range range)
{
return Range_seq(range.first, range.interval, (range.last - range.first) / range.interval + 1 );
}
int main() {
std::array<Range, 2> ranges = { { { 2,14,3 },{ 2,18,2 } } };
std::array<Range_seq, 2> arithmetic_sequences;
std::transform(begin(ranges), end(ranges), begin(arithmetic_sequences), range2seq);
std::vector<size_t> results;
arithmetic_sequence_union(
arithmetic_sequences,
[&results](auto item) {std::cout << item << "; "; }
);
return 0;
}
// output: 2; 4; 5; 6; 8; 10; 11; 12; 14; 16; 18;

How do I insert two values into a <list> or <vector> so that I can retrieve values later on?

Code worked on:
#include <algorithm>
#include <list>
#include <vector>
class key_value_sequences {
public:
int size(int key);
int * data(int key);
void insert(int key, int value);
private:
list< pair<int, vector<int> > > myList;
}; // class key_value_sequences
#endif
void key_value_sequences::insert(int key, int value){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
it->second.push_back(value);
return;
}
}
vector<int> v;
v.push_back(value);
myList.push_back(make_pair(key, v));
return;
};
int * key_value_sequences::data(int key){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
return (it->second);
}
}
vector<int> v;
return v;
};
int key_value_sequences::size(int key){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
return it->second.size();
}
}
return -1;
};
I am getting errors for template arguments, and can't figure out why. It looks like this line
std::list< pair<int, vector<int> > > myList;
is throwing errors
error: template argument 1 is invalid
std::list< pair<int, vector<int> > > myList;
^
error: template argument 2 is invalid
error: expected unqualified-id before ‘>’ token
std::list< pair<int, vector<int> > > myList;
^
I can't figure out why.
I'm also stuck with errors
/usr/include/c++/5/bits/stl_algobase.h:840:58: error: no type named ‘value_type’ in ‘struct std::iterator_traits<std::vector<int> >’
typedef typename iterator_traits<_II2>::value_type _ValueType2;
^
/usr/include/c++/5/bits/stl_algobase.h:845:9: error: no type named ‘value_type’ in ‘struct std::iterator_traits<std::vector<int> >’
&& __are_same<_ValueType1, _ValueType2>::__value);
^
The instantiation of the iterator is:
list<pair<int, vector<int>>>::iterator it;
Edit trying out vector hashtable:
class key_value_sequences {
public:
int size(int key);
int* data(int key);
void insert(int key, int value);
private:
vector<list<pair<int,int>>> hash_table;
list<pair<int, vector<int>>>::iterator it;
int hash(int value)
{
return abs(value%static_cast<int>(hash_table.size()));
}
}; // class key_value_sequences
#endif // A3_HPP
void key_value_sequences::insert(int key, int value){
list<pair<int,int>> &collisionlist = hash_table[hash(key)];
for (std::pair<int,int> &test: collisionlist)
{
if (key == test.first)
{
test.second = value; // update existing
return;
}
}
collisionlist.push_back(pair<int,int>(key, value));
};
int* key_value_sequences::data(int key){
for(it = hash_table.begin(); it != hash_table.end(); ++it){
if (it->first == key){
return &(it->second[0]);
}
}
return nullptr;
};
int key_value_sequences::size(int key){
for(it = hash_table.begin(); it != hash_table.end(); ++it){
if (it->first == key){
return it->second.size();
}
}
return -1;
};
Tried adding as much detail as I could in the comments, but this successfully passed all tests on my end. While I mentioned I reduced the original copy constructor from O(keys.length + vals.size) to just O(vals.size)- I lied.
resize() is linear in the length of the vector- so it's best to leave that alone.
#include <iostream>
#include <vector>
#include <list>
using namespace std;
class key_value_sequences{
public:
int size(int key);
int * data(int key);
void insert(int key, int value);
key_value_sequences(){}; //ctor
key_value_sequences(const key_value_sequences &_rhs); //[heli]coptor
~key_value_sequences(){}; //dtor
private:
vector <vector<int> *> keys;
list <vector<int> > vals;
};
key_value_sequences::key_value_sequences(const key_value_sequences &_rhs){
keys.resize(_rhs.keys.size()); //resize new kvs key vector to old size
auto it = _rhs.vals.begin();
while (it != _rhs.vals.end()){
vals.push_back(*it); //push back value vector to list
keys[(*it)[0]] = &vals.back(); //use the prepended key value of value vector
++it; // to reestablish ref in key vector
}
}
void key_value_sequences::insert(int key, int value){
if (key > -1 && key + 1 > keys.size()){ //if key index is valid & > key vector size
keys.resize(key+1, new vector<int>); //resize the vector to make room
vector<int> v;
vals.push_back(v); //push back new value vector to list
vals.back().push_back(key); //create key # front of list for the [heli]coptor
vals.back().push_back(value); //push back initial value
keys[key] = &vals.back(); //update reference in key vector
}
else if (key > -1){
keys[key]->push_back(value); //index already exists, push back value to value vector
}
return;
}
int * key_value_sequences::data(int key){
if (key + 1 > keys.size() || key < 0){
return nullptr;
}
else{
return &keys[key]->at(1); //if index is valid: return second element of value vector
} //in order to account for the prepended key
}
int key_value_sequences::size(int key){
if (key < 0 || keys[key]->empty() || key + 1 > keys.size()){
return -1;
}
else{
return keys[key]->size() - 1; //if index is valid: return size - 1 to account for key
}
}
To answer the title of your question, you can use the push_back methods of std::list and std::vector to put items into those containers.
The items will stay in the container until the container is deleted, the items are deleted or your program stops executing.
To find items in your containers, you can search using a loop. The std::list and std::vector both support iterators for iterating through the container. Items in a std::vector can be retrieved using array syntax.
It sounds to me like you need a multimap. A map is a container that allows you to insert key / value pairs, where keys can be used to look up values. A multimap allows you to have multiple values associated with a single key.
For example:
std::multimap<int, int> myMap;
myMap.insert( std::make_pair( 0, 8 ) );
myMap.insert( std::make_pair( 0, 5 ) );
myMap.insert( std::make_pair( 0, 7 ) );
myMap.insert( std::make_pair( 1, 15 ) );
// equal_range() returns a pair of iterators pointing to the first item
// in the list for the specified key, and one past the final item containing
// the key.
auto searchResultIteratorPair = myMap.equal_range( 0 );
// Print out the values found
for( auto it = searchResultIteratorPair.first; it != searchResultIteratorPair.second; it++ )
{
std::cout << "Value: " << it->second << std::endl;
}
If my assumption was wrong and you really did want to use a list / vector, then you would need to create them as a list / vector of pairs. Then to find items you would iterate the entire list and check each pair to see if it matched your criteria.
For example:
std::list< std::pair<int, int> > myList;
myList.push_back( std::make_pair( 0, 8 ) );
myList.push_back( std::make_pair( 0, 5 ) );
myList.push_back( std::make_pair( 0, 7 ) );
myList.push_back( std::make_pair( 1, 15 ) );
int searchValue = 0;
for( auto it = myList.begin(); it != myList.end(); it++ )
{
if( it->first != searchValue )
continue;
std::cout << "Value: " << it->second << std::endl;
}

BGL : get vertex descriptor with data

I want to get the vertex descriptor with the composant of the vertex, like this :
struct WayPoint{
std::pair<float, float> pos; // with this composant
};
the adjency list :
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::undirectedS,
WayPoint,
WayPointConnection
> WayPointGraph;
typedef WayPointGraph::vertex_descriptor WayPointID;
typedef WayPointGraph::edge_descriptor WayPointConnectionID;
I built my graph and created all the vertices / edges .... the aim is to apply an astar on the graph.
void PathFinding::findMeAPath(std::pair<float, float>begin, std::pair<float, float>end)
{
std::vector<WayPointID> p(boost::num_vertices(graphe));
std::vector<float> d(boost::num_vertices(graphe));
WayPointID start = // I want to find the WayPointID with begin
WayPointID goal = //same with end;
shortest_path.clear();
try {
boost::astar_search
(
graphe,
start,
boost::astar_heuristic<WayPointGraph, float>(),
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(astar_goal_visitor(goal)).weight_map(boost::get(&WayPointConnection::dist, graphe))
);
} catch(found_goal fg) {
for(WayPointID v = goal;; v = p[v]) {
shortest_path.push_front(v);
if(p[v] == v)
break;
}
}
}
You need to write a function to find a vertex given a position. The graph type that you have defined uses std::vector to store vertices, so the function will have to iterate through it and compare the queried position to each WayPoint. Something like this could do:
std::pair<WayPointID, bool> find_vertex(const WayPoint& wp, const WayPointGraph& graph)
{
for (WayPointID id = 0; id < boost::num_vertices(graph); ++id)
{
if (equal(graph[id], wp))
return std::make_pair(id, true);
}
return std::make_pair(0, false);
}
Note that the function returns a pair (Id + boolean flag) to indicate whether the search succeeded or not, so you would use it as follows:
bool vertex_found;
WayPointID start;
std::tie (start, vertex_found) = find_vertex(begin, graphe);
if (!vertex_found)
// do something about it
Also the function uses the following to compare positions:
bool equal(const std::pair<float, float>& p1, const std::pair<float, float>& p2)
{
const float EPS = 1e-6;
return (std::fabs(p1.first - p2.first) < EPS &&
std::fabs(p1.second - p2.second) < EPS);
}

Sorting a std::map by value before output & destroy

I'm aware that map is not prepared to be sorted. It's heavily optimized for fast and random key access and actually doesn't support std::sort.
My current problem is that I've a full map<std::string,int> which I'm not going to use anymore. I just need to extract 10 pairs in value(int) order and destroy it.
The best thing, if it was possible, would be to sort it in place and then iterate it 10 times, but that apparently is not a solution.
I'm trying different solutions as going through a multimap<int,string> (to allow duplicate keys), but I'd like to know if there is a more elegant solution, using stl algorithms as much as posible.
EDIT:
I'm using a map because for the 99% of the time, I need it as a map: fast key lookups to increase values. Just need a good way of later extracting in value order when I don't need the map anymore.
Current approach whould be:
std::copy the map(std::string,int) to a vector(pair(std::string,int))
sort the vector
get the first 10 values
destroy vector and map
Maps are stored as a tree sorted in key order. You want the 10 smallest (or largest) integer values, and their keys, right?
In that case, iterate the map and put all the key-value pairs in a vector of pairs (std::vector<std::pair<std::string, int> >). I think you can just use the two-iterator-arg constructor of std::vector for this. Then use std::partial_sort on the vector. Specify a comparator to partial_sort, which compares pairs by just comparing the value int, ignoring the key string. Then you have the 10 pairs you want at the start of the vector, and the rest of the vector contains the remaining pairs in an unspecified order.
Code (untested):
typedef std::pair<std::string, int> mypair;
struct IntCmp {
bool operator()(const mypair &lhs, const mypair &rhs) {
return lhs.second < rhs.second;
}
};
void print10(const std::map<std::string,int> &mymap) {
std::vector<mypair> myvec(mymap.begin(), mymap.end());
assert(myvec.size() >= 10);
std::partial_sort(myvec.begin(), myvec.begin() + 10, myvec.end(), IntCmp());
for (int i = 0; i < 10; ++i) {
std::cout << i << ": " << myvec[i].first
<< "-> " << myvec[i].second << "\n";
}
}
Note that if there are several strings with the same value, either side of the limit of 10, then it's not specified which ones you get. You can control this by having your comparator look at the string too, in cases where the integers are equal.
For iterating by value you could use boost::multi_index. It will looks as follows:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
using namespace boost::multi_index;
struct X {
X( std::string val_str, int val_int ) : val_str(val_str), val_int(val_int) {};
std::string val_str;
int val_int;
};
typedef multi_index_container<
X,
indexed_by<
hashed_unique< member<X, std::string, &X::val_str> >,
ordered_non_unique< member<X, int, &X::val_int> >
>
> X_map;
void func()
{
X_map data;
data.insert( X("test", 1) );
// ...
// search by val_str
// complexity is equal to O(1) for hashed index (worst cast O(n) ),
// and O(log n) for ordered index
X_map::const_iterator it = data.find( "test" );
// ...
// iterate in order of val_int
size_t N = 0;
for ( X_map::nth_index<1>::type::const_iterator it = data.get<1>().begin(); N < 10 && it != data.get<1>().end(); ++it, ++N ) {
// copy elements somewhere
}
}
You could use any index for iteration ( val_str or val_int ).
May not be the most elegant way, but you can sort them via value in a set as:
#include <map>
#include <set>
#include <iostream>
#include <string>
using namespace std;
struct sortPairSecond
{
bool operator()(const pair<string, int> &lhs, const pair<string, int> &rhs)
{
return lhs.second < rhs.second;
}
};
int main (int argc, char *argv[])
{
cout << "Started...\n";
map<string, int> myMap;
myMap["One"] = 1;
myMap["Ten"] = 10;
myMap["Five"] = 5;
myMap["Zero"] = 0;
myMap["Eight"] = 8;
cout << "Map Order:\n---------------\n";
set<pair<string,int>, sortPairSecond > mySet;
for(map<string, int>::const_iterator it = myMap.begin(); it != myMap.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
mySet.insert(*it);
}
cout << "\nSet Order:\n--------------\n";
for(set<pair<string, int> >::const_iterator it = mySet.begin(); it != mySet.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
}
return 1;
}
If you iterate using the map iterator, you will get the items sorted on key as it internally uses balanced binary tree to store the values. So you could just extract the 10 values from it using the iterators. Is that what you want or you want to do something else? Please clarify.
EDIT:
Instead of using the vector and sorting, you can directly use set and pass the comparison function. Then you can extract the top 10 elements. This is my test code:
typedef std::pair<std::string, int> MyPair;
struct MyTestCompare
{
bool operator()(const MyPair& firstPair, const MyPair& secondPair) const
{
return firstPair.second < secondPair.second;
}
};
int main()
{
std::map<std::string, int> m;
m[std::string("1")] = 10;
m[std::string("2")] = 40;
m[std::string("3")] = 30;
m[std::string("4")] = 20;
std::set<MyPair,MyTestCompare> s;
std::map<std::string, int>::iterator iter = m.begin();
std::map<std::string, int>::iterator endIter = m.end();
for(; iter != endIter; ++iter)
{
s.insert(*iter);
}
}
Another possibility is to build a reverse map. For you that would be std::map<int, std::string>. Entries in the reverse map are sorted by their value.
The following is what I have in my tool box for such occasions:
template< typename TK, typename TV, class TP, class TA, typename T1, typename T2 >
inline void asserted_insert(std::map<TK,TV,TP,TA>& m, const T1& k, const T2& v)
{
typedef std::map<TK,TV,TP,TA> map_type;
typedef typename map_type::value_type value_type;
assert( m.insert(value_type(k,v)).second );
}
template< class TMap > struct reverse_map;
template< typename T1, typename T2 > struct reverse_map< std::map<T1,T2> > {
typedef std::map<T2,T1> result_t;
};
template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 >
inline void build_reverse_map(const std::map<T1,T2,TP1,TA1>& map, std::map<T2,T1,TP2,TA2>& reverse_map)
{
typedef std::map<T1,T2,TP1,TA1> map_type;
for( typename map_type::const_iterator it=map.begin(),
end=map.end(); it!=end; ++it ) {
asserted_insert( reverse_map, it->second, it->first );
}
}
This code assumes that values are unique, too (and throws an assertion, if this is not the case). If this doesn't apply to your problem, you could easily change the code to use a multi map.