Related
Is there a prettier way to get a value from inside a vector in a map without using loops?
For example , How to get the key of the vector which has an element of value 50?
std::map < int , std::vector < int >> myMap;
std::vector < int > a= {10,20,30};
std::vector < int > b= {50,60,70};
myMap.insert({1000,a});
myMap.insert({2000,b});
Can I get the key(s) if the value is in more than one vector ?
For Example if 50 Is in both vectors ?
std::vector < int > a= {10,20,50};
std::vector < int > b= {50,60,70};
Here works for every map, value and inner container:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
#include <optional>
template <typename ValueT, typename MapT>
std::optional<typename MapT::key_type> find_key_for_value_contained_in_intern_container(const ValueT& value, const MapT& map) {
auto key_it = std::find_if(std::begin(map), std::end(map), [&value] (const auto & element) {
return std::find(std::begin(element.second), std::end(element.second), value) != std::end(element.second);
});
return (key_it != std::end(map)) ? key_it->first : std::optional<typename MapT::key_type>();
}
int main() {
std::map<int, std::vector<int >> myMap;
std::vector<int> a = {10, 20, 30};
std::vector<int> b = {50, 60, 70};
myMap.insert({1000, a});
myMap.insert({2000, b});
int value_to_search{50};
auto key = find_key_for_value_contained_in_intern_container(value_to_search, myMap);
if (key)
std::cout << "Value " << value_to_search << " is at key " << *key << "\n";
}
Is there a prettier way to get a value from inside a vector in a map without using loops?
You cannot avoid looping the map. But you can avoid writing the loop by using standard algorithm: std::find_if.
I mean more efficient
If you want the lookup to be efficient, then your choice of data structure is not good. You could instead use following:
std::unordered_multimap<int, int> myMap{
{10, 1000},
{20, 1000},
{30, 1000},
{50, 2000},
{60, 2000},
{70, 2000},
};
With this, the lookup has asymptotically constant complexity instead of linear.
If you need to be efficient to find the key from the value, and conversely, you could use two maps. If needed, you can encapsulate that into a single class. You are then paying for efficiency of search with space.
Something along the lines of:
class DoubleMap {
public:
DoubleMap() {}
void insert(int key, std::vector<int> value) {
myMap1.insert({key, value});
myMap2.insert({value, key});
}
std::vector<int> getValue (int key) {return myMap1[key];}
int getKey(std::vector<int> value) {return myMap2[value];}
private :
std::map <int, std::vector<int>> myMap1;
std::map<std::vector<int>, int> myMap2;
}
I wouldn't advise this kind of heavy class unless you really need the efficiency of search in both directions, though.
Say I have the following setup:
I have a boost::geometry::index::rtree that takes as a key a two-dimensional box and as a value a points.
The first dimension of the box, will in practice be applied to (real-valued closed) intervals, whereas the second only to point.
So my box looks like:
using namespace std;
typedef bg::model::point<unsigned long, 2, bg::cs::cartesian> _pt_t;
typedef bg::model::box<_pt_t> _box_t;
typedef pair<_box_t, unsigned long> tree_v_t;
typedef bgi::rtree<tree_v_t, bgi::quadratic<16> > rtree_t;
A box will allways be initialized using:
_box_t _mb(unsigned long i, unsigned long s, unsigned long d){
_box_t b(_pt_t(s, i), _pt_t(s + d, i));
return b;
}
Now let's say I have initialised the rtree and I want to do two sorts of complicate queries:
given a set si of intervalsset<pair<unsigned int, unsigned int> > and a set sp of points set<unsigned int>, I would like to iterate all the values that are the result of the following pseudocode query:
any(si, intersect(rtree_level1)) &&
any(sp, contains(rtree_level2)) &&
value in ps
In other words I want the subtree of the rtree that contains the intersection of intervals contained in si and of points contained in sp and that its values are also in sp. If you like you can assume that all intervals in si are disjoint.
given a set spi of points and intervals set<unsigned int, pair<unsigned int, unsigned int> >, I would like to iterate all the values that are the result of the following pseudocode query:
any(spi, intersect(rtree_level1)(spi.interval) &&
contains(rtree_level2)(spi.point) &&
value in spi.point
)
In other words I want the union of all subtrees that come from each element of spi, for which they are the intersection of the given interval and they contain only those points both as keys (second level) and values. This is like the union all the R-trees produced from query 1 if both si and sp have one element.
I can understand how I can do that using the satisfy predicate and applying transform to the iterator produced by qbegin, but
what is the most efficient way to do that in boost?
Here is a basic program with double index (R-tree and std::map) doing bi-directional indexing: from char to box/interval and from box/interval to char:
Includes, iostream is needed only for output.
#include <boost/geometry.hpp>
#include <map>
#include <vector>
#include <iostream>
Namespaces for convenience.
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
Basic bi-directional index allowing to insert box/interval-char pairs and find box based on char or vector of chars based on box (intersecting). insert() merges boxes if needed.
template <typename Box, typename T>
class rtree_map_index
{
typedef std::map<T, Box> map_type;
typedef typename map_type::iterator map_iterator;
typedef typename map_type::const_iterator map_const_iterator;
typedef std::pair<Box, map_iterator> rtree_value;
typedef bgi::rtree<rtree_value, bgi::rstar<4> > rtree_type;
public:
void insert(Box const& box, T const& v)
{
std::pair<map_iterator, bool>
p = m_map.insert(std::make_pair(v, box));
map_iterator map_it = p.first;
T const& map_val = map_it->first;
Box & map_box = map_it->second;
// new key,value inserted into map
if (p.second)
{
// insert it to the r-tree
m_rtree.insert(rtree_value(map_box, map_it));
}
// key already exists in map and box has to be expanded
else if (! bg::covered_by(box, map_box))
{
// calculate expanded box
Box new_box = map_box;
bg::expand(new_box, box);
// update r-tree
m_rtree.remove(rtree_value(map_box, map_it));
m_rtree.insert(rtree_value(new_box, map_it));
// update map
map_box = new_box;
}
}
bool find(T const& v, Box & result) const
{
map_const_iterator it = m_map.find(v);
if (it != m_map.end())
{
result = it->second;
return true;
}
return false;
}
void find(Box const& box, std::vector<char> & result) const
{
std::vector<rtree_value> res;
m_rtree.query(bgi::intersects(box), std::back_inserter(res));
result.resize(res.size());
for (size_t i = 0; i < res.size(); ++i)
result[i] = res[i].second->first;
}
private:
rtree_type m_rtree;
map_type m_map;
};
Main function with basic use-case.
int main()
{
for 2-dimentional data stored in the r-tree (boxes).
{
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
rtree_map_index<box, char> index;
index.insert(box(point(0, 0), point(3, 3)), 'a');
index.insert(box(point(1, 1), point(4, 4)), 'a');
index.insert(box(point(5, 5), point(6, 6)), 'b');
box res1;
index.find('a', res1);
std::cout << bg::wkt(res1) << std::endl;
std::vector<char> res2;
index.find(box(point(4, 4), point(5, 5)), res2);
BOOST_ASSERT(res2.size() == 2);
std::cout << res2[0] << std::endl;
std::cout << res2[1] << std::endl;
}
for 1-dimensional data stored in the r-tree (intervals)
{
typedef bg::model::point<double, 1, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
rtree_map_index<box, char> index;
index.insert(box(point(0), point(3)), 'a');
index.insert(box(point(1), point(4)), 'a');
index.insert(box(point(5), point(6)), 'b');
box res1;
index.find('a', res1);
std::cout << "(" << bg::get<0, 0>(res1) << ", " << bg::get<1, 0>(res1) << ")" << std::endl;
std::vector<char> res2;
index.find(box(point(4), point(5)), res2);
BOOST_ASSERT(res2.size() == 2);
std::cout << res2[0] << std::endl;
std::cout << res2[1] << std::endl;
}
The end.
return 0;
}
Note that instead of rtree you could use interval_map. You should be able to build on top of rtree_map_index. You could add a constructor creating map and rtree from a container of elements of type std::pair<Box, T> to take advantage from r-tree packing algorithm. You could implement whatever find() function you need. Etc.
This question already has answers here:
How can I sort two vectors in the same way, with criteria that uses only one of the vectors?
(9 answers)
Closed 9 months ago.
I have several std::vector, all of the same length. I want to sort one of these vectors, and apply the same transformation to all of the other vectors. Is there a neat way of doing this? (preferably using the STL or Boost)? Some of the vectors hold ints and some of them std::strings.
Pseudo code:
std::vector<int> Index = { 3, 1, 2 };
std::vector<std::string> Values = { "Third", "First", "Second" };
Transformation = sort(Index);
Index is now { 1, 2, 3};
... magic happens as Transformation is applied to Values ...
Values are now { "First", "Second", "Third" };
friol's approach is good when coupled with yours. First, build a vector consisting of the numbers 1…n, along with the elements from the vector dictating the sorting order:
typedef vector<int>::const_iterator myiter;
vector<pair<size_t, myiter> > order(Index.size());
size_t n = 0;
for (myiter it = Index.begin(); it != Index.end(); ++it, ++n)
order[n] = make_pair(n, it);
Now you can sort this array using a custom sorter:
struct ordering {
bool operator ()(pair<size_t, myiter> const& a, pair<size_t, myiter> const& b) {
return *(a.second) < *(b.second);
}
};
sort(order.begin(), order.end(), ordering());
Now you've captured the order of rearrangement inside order (more precisely, in the first component of the items). You can now use this ordering to sort your other vectors. There's probably a very clever in-place variant running in the same time, but until someone else comes up with it, here's one variant that isn't in-place. It uses order as a look-up table for the new index of each element.
template <typename T>
vector<T> sort_from_ref(
vector<T> const& in,
vector<pair<size_t, myiter> > const& reference
) {
vector<T> ret(in.size());
size_t const size = in.size();
for (size_t i = 0; i < size; ++i)
ret[i] = in[reference[i].first];
return ret;
}
typedef std::vector<int> int_vec_t;
typedef std::vector<std::string> str_vec_t;
typedef std::vector<size_t> index_vec_t;
class SequenceGen {
public:
SequenceGen (int start = 0) : current(start) { }
int operator() () { return current++; }
private:
int current;
};
class Comp{
int_vec_t& _v;
public:
Comp(int_vec_t& v) : _v(v) {}
bool operator()(size_t i, size_t j){
return _v[i] < _v[j];
}
};
index_vec_t indices(3);
std::generate(indices.begin(), indices.end(), SequenceGen(0));
//indices are {0, 1, 2}
int_vec_t Index = { 3, 1, 2 };
str_vec_t Values = { "Third", "First", "Second" };
std::sort(indices.begin(), indices.end(), Comp(Index));
//now indices are {1,2,0}
Now you can use the "indices" vector to index into "Values" vector.
Put your values in a Boost Multi-Index container then iterate over to read the values in the order you want. You can even copy them to another vector if you want to.
Only one rough solution comes to my mind: create a vector that is the sum of all other vectors (a vector of structures, like {3,Third,...},{1,First,...}) then sort this vector by the first field, and then split the structures again.
Probably there is a better solution inside Boost or using the standard library.
You can probably define a custom "facade" iterator that does what you need here. It would store iterators to all your vectors or alternatively derive the iterators for all but the first vector from the offset of the first. The tricky part is what that iterator dereferences to: think of something like boost::tuple and make clever use of boost::tie. (If you wanna extend on this idea, you can build these iterator types recursively using templates but you probably never want to write down the type of that - so you either need c++0x auto or a wrapper function for sort that takes ranges)
I think what you really need (but correct me if I'm wrong) is a way to access elements of a container in some order.
Rather than rearranging my original collection, I would borrow a concept from Database design: keep an index, ordered by a certain criterion. This index is an extra indirection that offers great flexibility.
This way it is possible to generate multiple indices according to different members of a class.
using namespace std;
template< typename Iterator, typename Comparator >
struct Index {
vector<Iterator> v;
Index( Iterator from, Iterator end, Comparator& c ){
v.reserve( std::distance(from,end) );
for( ; from != end; ++from ){
v.push_back(from); // no deref!
}
sort( v.begin(), v.end(), c );
}
};
template< typename Iterator, typename Comparator >
Index<Iterator,Comparator> index ( Iterator from, Iterator end, Comparator& c ){
return Index<Iterator,Comparator>(from,end,c);
}
struct mytype {
string name;
double number;
};
template< typename Iter >
struct NameLess : public binary_function<Iter, Iter, bool> {
bool operator()( const Iter& t1, const Iter& t2 ) const { return t1->name < t2->name; }
};
template< typename Iter >
struct NumLess : public binary_function<Iter, Iter, bool> {
bool operator()( const Iter& t1, const Iter& t2 ) const { return t1->number < t2->number; }
};
void indices() {
mytype v[] = { { "me" , 0.0 }
, { "you" , 1.0 }
, { "them" , -1.0 }
};
mytype* vend = v + _countof(v);
Index<mytype*, NameLess<mytype*> > byname( v, vend, NameLess<mytype*>() );
Index<mytype*, NumLess <mytype*> > bynum ( v, vend, NumLess <mytype*>() );
assert( byname.v[0] == v+0 );
assert( byname.v[1] == v+2 );
assert( byname.v[2] == v+1 );
assert( bynum.v[0] == v+2 );
assert( bynum.v[1] == v+0 );
assert( bynum.v[2] == v+1 );
}
A slightly more compact variant of xtofl's answer for if you are just looking to iterate through all your vectors based on the of a single keys vector. Create a permutation vector and use this to index into your other vectors.
#include <boost/iterator/counting_iterator.hpp>
#include <vector>
#include <algorithm>
std::vector<double> keys = ...
std::vector<double> values = ...
std::vector<size_t> indices(boost::counting_iterator<size_t>(0u), boost::counting_iterator<size_t>(keys.size()));
std::sort(begin(indices), end(indices), [&](size_t lhs, size_t rhs) {
return keys[lhs] < keys[rhs];
});
// Now to iterate through the values array.
for (size_t i: indices)
{
std::cout << values[i] << std::endl;
}
ltjax's answer is a great approach - which is actually implemented in boost's zip_iterator http://www.boost.org/doc/libs/1_43_0/libs/iterator/doc/zip_iterator.html
It packages together into a tuple whatever iterators you provide it.
You can then create your own comparison function for a sort based on any combination of iterator values in your tuple. For this question, it would just be the first iterator in your tuple.
A nice feature of this approach is that it allows you to keep the memory of each individual vector contiguous (if you're using vectors and that's what you want). You also don't need to store a separate index vector of ints.
This would have been an addendum to Konrad's answer as it an approach for a in-place variant of applying the sort order to a vector. Anyhow since the edit won't go through I will put it here
Here is a in-place variant with a slightly higher time complexity that is due to a primitive operation of checking a boolean. The additional space complexity is of a vector which can be a space efficient compiler dependent implementation. The complexity of a vector can be eliminated if the given order itself can be modified.
Here is a in-place variant with a slightly higher time complexity that is due to a primitive operation of checking a boolean. The additional space complexity is of a vector which can be a space efficient compiler dependent implementation. The complexity of a vector can be eliminated if the given order itself can be modified. This is a example of what the algorithm is doing.
If the order is 3 0 4 1 2, the movement of the elements as indicated by the position indices would be 3--->0; 0--->1; 1--->3; 2--->4; 4--->2.
template<typename T>
struct applyOrderinPlace
{
void operator()(const vector<size_t>& order, vector<T>& vectoOrder)
{
vector<bool> indicator(order.size(),0);
size_t start = 0, cur = 0, next = order[cur];
size_t indx = 0;
T tmp;
while(indx < order.size())
{
//find unprocessed index
if(indicator[indx])
{
++indx;
continue;
}
start = indx;
cur = start;
next = order[cur];
tmp = vectoOrder[start];
while(next != start)
{
vectoOrder[cur] = vectoOrder[next];
indicator[cur] = true;
cur = next;
next = order[next];
}
vectoOrder[cur] = tmp;
indicator[cur] = true;
}
}
};
Here is a relatively simple implementation using index mapping between the ordered and unordered names that will be used to match the ages to the ordered names:
void ordered_pairs()
{
std::vector<std::string> names;
std::vector<int> ages;
// read input and populate the vectors
populate(names, ages);
// print input
print(names, ages);
// sort pairs
std::vector<std::string> sortedNames(names);
std::sort(sortedNames.begin(), sortedNames.end());
std::vector<int> indexMap;
for(unsigned int i = 0; i < sortedNames.size(); ++i)
{
for (unsigned int j = 0; j < names.size(); ++j)
{
if (sortedNames[i] == names[j])
{
indexMap.push_back(j);
break;
}
}
}
// use the index mapping to match the ages to the names
std::vector<int> sortedAges;
for(size_t i = 0; i < indexMap.size(); ++i)
{
sortedAges.push_back(ages[indexMap[i]]);
}
std::cout << "Ordered pairs:\n";
print(sortedNames, sortedAges);
}
For the sake of completeness, here are the functions populate() and print():
void populate(std::vector<std::string>& n, std::vector<int>& a)
{
std::string prompt("Type name and age, separated by white space; 'q' to exit.\n>>");
std::string sentinel = "q";
while (true)
{
// read input
std::cout << prompt;
std::string input;
getline(std::cin, input);
// exit input loop
if (input == sentinel)
{
break;
}
std::stringstream ss(input);
// extract input
std::string name;
int age;
if (ss >> name >> age)
{
n.push_back(name);
a.push_back(age);
}
else
{
std::cout <<"Wrong input format!\n";
}
}
}
and:
void print(const std::vector<std::string>& n, const std::vector<int>& a)
{
if (n.size() != a.size())
{
std::cerr <<"Different number of names and ages!\n";
return;
}
for (unsigned int i = 0; i < n.size(); ++i)
{
std::cout <<'(' << n[i] << ", " << a[i] << ')' << "\n";
}
}
And finally, main() becomes:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
void ordered_pairs();
void populate(std::vector<std::string>&, std::vector<int>&);
void print(const std::vector<std::string>&, const std::vector<int>&);
//=======================================================================
int main()
{
std::cout << "\t\tSimple name - age sorting.\n";
ordered_pairs();
}
//=======================================================================
// Function Definitions...
**// C++ program to demonstrate sorting in vector
// of pair according to 2nd element of pair
#include <iostream>
#include<string>
#include<vector>
#include <algorithm>
using namespace std;
// Driver function to sort the vector elements
// by second element of pairs
bool sortbysec(const pair<char,char> &a,
const pair<int,int> &b)
{
return (a.second < b.second);
}
int main()
{
// declaring vector of pairs
vector< pair <char, int> > vect;
// Initialising 1st and 2nd element of pairs
// with array values
//int arr[] = {10, 20, 5, 40 };
//int arr1[] = {30, 60, 20, 50};
char arr[] = { ' a', 'b', 'c' };
int arr1[] = { 4, 7, 1 };
int n = sizeof(arr)/sizeof(arr[0]);
// Entering values in vector of pairs
for (int i=0; i<n; i++)
vect.push_back( make_pair(arr[i],arr1[i]) );
// Printing the original vector(before sort())
cout << "The vector before sort operation is:\n" ;
for (int i=0; i<n; i++)
{
// "first" and "second" are used to access
// 1st and 2nd element of pair respectively
cout << vect[i].first << " "
<< vect[i].second << endl;
}
// Using sort() function to sort by 2nd element
// of pair
sort(vect.begin(), vect.end(), sortbysec);
// Printing the sorted vector(after using sort())
cout << "The vector after sort operation is:\n" ;
for (int i=0; i<n; i++)
{
// "first" and "second" are used to access
// 1st and 2nd element of pair respectively
cout << vect[i].first << " "
<< vect[i].second << endl;
}
getchar();
return 0;`enter code here`
}**
with C++11 lambdas and the STL algorithms based on answers from Konrad Rudolph and Gabriele D'Antona:
template< typename T, typename U >
std::vector<T> sortVecAByVecB( std::vector<T> & a, std::vector<U> & b ){
// zip the two vectors (A,B)
std::vector<std::pair<T,U>> zipped(a.size());
for( size_t i = 0; i < a.size(); i++ ) zipped[i] = std::make_pair( a[i], b[i] );
// sort according to B
std::sort(zipped.begin(), zipped.end(), []( auto & lop, auto & rop ) { return lop.second < rop.second; });
// extract sorted A
std::vector<T> sorted;
std::transform(zipped.begin(), zipped.end(), std::back_inserter(sorted), []( auto & pair ){ return pair.first; });
return sorted;
}
So many asked this question and nobody came up with a satisfactory answer. Here is a std::sort helper that enables to sort two vectors simultaneously, taking into account the values of only one vector. This solution is based on a custom RadomIt (random iterator), and operates directly on the original vector data, without temporary copies, structure rearrangement or additional indices:
C++, Sort One Vector Based On Another One
I'm new to stl in c++ and I want to iterate over a vector of maps. Then, if one of these maps satisfies a certain condition, I want to make a copy of this map an then insert the copy into the vector.
For example:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
for(vector<map<string, int> >::iterator iter1 = my_vector.begin();
iter1 != my_vector,end();
iter1++){
for(map<string, int>::iterator iter2 = (*iter1).begin();
iter2 != (*iter1).end();
iter2++){
if(iter2->second == 0){
// make a copy of the map (*iter1), make some changes on it,
// then insert the copy in the vector
}
}
}
return 0;
}
I have tried with:
map<string, int> new_map = *iter1;
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
But the program crashes. No compiler error, only a program crash.
Then I found this question and answers in stackoverflow, so I tried another way like this:
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
int remaining = my_vector.size();
int current_position = 0;
while(remaining>0){
for(map<string, int>:: iterator iter1 = my_vector[index].begin();
iter1 != my_vector[index].end();
iter1++){
if(iter1->second == 0){
map<string, int> new_map = my_vector[0];
new_map["zero"]++; // to avoid an infinite loop
my_vector.push_back(new_map);
}
}
index++;
remaining = my_vector.size()-index;
}
return 0;
}
But the program is still crashing, so I think the problem (or one of the problems) would be not only the iterator, but also the "copy" of the map.
If anyone have an idea of how I should do this, I will really appreciate it.
Your program may crash because push_back can invalidate iterators. For example if a call to push_back leads to memory reallocation (it happens when you exceed current capacity of the vector) then all vector's iterators become invalid (they point to deallocated memory).
To solve that problem you can use indexes instead of iterators to access the vector's elements or you can push_back new elements to another copy of the vector.
In other words: don't use an old vector::iterator after you push_back'ed a new element into the vector.
One way you can do is:
size_t orig_size = my_vector.size();
for( size_t i = 0; i < orig_size; i++ ) {
//...
}
Other way:
Build a new vector and then afterwards append the contents of this new vector to the original vector.
I probably have the wrong idea, but I think you can do something like this. You iterate over the elements of the vector, use the predicate to see if a current element fits the criteria, then use back_inserter.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_vector.push_back(my_map);
auto predicate = [](const map<string, int>& cur) {
for (const auto& pair : cur)
{
if (pair.second == 0)
return true;
}
return false;
};
std::copy_if(my_vector.begin(), my_vector.end(), back_inserter(my_vector), predicate);
return 0;
}
This is my solution. It uses C++11 (because it looks so much better). Especially with collections C++11 gives the workflow a real boost.
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std ;
void merge(vector<map<string,int>>& from, vector<map<string,int>>& to)
{
for(auto entry : from) {
to.push_back(entry) ;
}
}
void search(vector<map<string,int>>& vec)
{
int iCount = 0 ;
vector<map<string, int>> cVector ;
for(auto vmap : vec) {
for(auto mapentry : vmap) {
if(mapentry.second == 0) {
iCount++ ;
map<string, int> new_map = vmap ;
cVector.push_back(new_map) ;
}
}
}
cout << "iCount: " << iCount << endl ;
merge(cVector, vec) ;
}
int main(){
vector<map<string, int> > my_vector;
map<string, int> my_map;
my_map["zero"] = 0;
my_map["third"] = 2 ;
my_map["second"] = 0 ;
my_vector.push_back(my_map);
cout << my_vector.size() << endl ;
vector<map<string,int>> cVector ;
search(my_vector) ;
cout << my_vector.size() << endl ;
return 0;
}
As written by Alex Antonov the push_back invalidates your Iterator because the memory gets reallocated. My answer creates a new vector and copies the entries after the search back in the original vector (my_vector).
About range base for-loops see this.
How can i get the top n keys of std::map based on their values?
Is there a way that i can get a list of say for example the top 10 keys with the biggest value as their values?
Suppose we have a map similar to this :
mymap["key1"]= 10;
mymap["key2"]= 3;
mymap["key3"]= 230;
mymap["key4"]= 15;
mymap["key5"]= 1;
mymap["key6"]= 66;
mymap["key7"]= 10;
And i only want to have a list of top 10 keys which has a bigger value compared to the other.
for example the top 4 for our mymap is
key3
key6
key4
key1
key10
note:
the values are not unique, actually they are the number of occurrences of each key. and i want to get a list of most occurred keys
note 2:
if map is not a good candidate and you want to suggest anything, please do it according to the c++11 ,i cant use boost at the time.
note3:
in case of using std::unordered_multimap<int,wstring> do i have any other choices?
The order of a map is based on its key and not its values and cannot be reordered so it is necessary to iterate over the map and maintain a list of the top ten encountered or as commented by Potatoswatter use partial_sort_copy() to extract the top N values for you:
std::vector<std::pair<std::string, int>> top_four(4);
std::partial_sort_copy(mymap.begin(),
mymap.end(),
top_four.begin(),
top_four.end(),
[](std::pair<const std::string, int> const& l,
std::pair<const std::string, int> const& r)
{
return l.second > r.second;
});
See online demo.
Choosing a different type of container may be more appropriate, boost::multi_index would be worth investigating, which:
... enables the construction of containers maintaining one or more indices with different sorting and access semantics.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, const char * argv[])
{
map<string, int> entries;
// insert some random entries
for(int i = 0; i < 100; ++i)
{
string name(5, 'A' + (char)(rand() % (int)('Z' - 'A') ));
int number = rand() % 100;
entries.insert(pair<string, int>(name, number));
}
// create container for top 10
vector<pair<string, int>> sorted(10);
// sort and copy with reversed compare function using second value of std::pair
partial_sort_copy(entries.begin(), entries.end(),
sorted.begin(), sorted.end(),
[](const pair<string, int> &a, const pair<string, int> &b)
{
return !(a.second < b.second);
});
cout << endl << "all elements" << endl;
for(pair<string, int> p : entries)
{
cout << p.first << " " << p.second << endl;
}
cout << endl << "top 10" << endl;
for(pair<string, int> p : sorted)
{
cout << p.first << " " << p.second << endl;
}
return 0;
}
Not only does std::map not sort by mapped-to value (such values need not have any defined sorting order), it doesn't allow rearrangement of its elements, so doing ++ map[ "key1" ]; on a hypothetical structure mapping the values back to the keys would invalidate the backward mapping.
Your best bet is to put the key-value pairs into another structure, and sort that by value at the time you need the backward mapping. If you need the backward mapping at all times, you would have to remove, modify, and re-add each time the value is changed.
The most efficient way to sort the existing map into a new structure is std::partial_sort_copy, as (just now) illustrated by Al Bundy.
since the mapped values are not indexed, you would have to read everything and select the 10 biggest values.
std::vector<mapped_type> v;
v.reserve(mymap.size());
for(const auto& Pair : mymap)
v.push_back( Pair.second );
std::sort(v.begin(), v.end(), std::greater<mapped_type>());
for(std::size_t i = 0, n = std::min<int>(10,v.size()); i < n; ++i)
std::cout << v[i] << ' ';
another way, is to use two maps or a bimap, thus mapped values would be ordered.
The algorithm you're looking for is nth_element, which partially sorts a range so that the nth element is where it would be in a fully sorted range. For example, if you wanted the top three items in descending order, you'd write (in pseudo C++)
nth_element(begin, begin + 3, end, predicate)
The problem is nth_element doesn't work with std::map. I would therefore suggest you change your data structure to a vector of pairs (and depending on the amount of data you're dealing with, you may find this to be a quicker data structure anyway). So, in the case of your example, I'd write it like this:
typedef vector<pair<string, int>> MyVector;
typedef MyVector::value_type ValueType;
MyVector v;
// You should use an initialization list here if your
// compiler supports it (mine doesn't...)
v.emplace_back(ValueType("key1", 10));
v.emplace_back(ValueType("key2", 3));
v.emplace_back(ValueType("key3", 230));
v.emplace_back(ValueType("key4", 15));
v.emplace_back(ValueType("key5", 1));
v.emplace_back(ValueType("key6", 66));
v.emplace_back(ValueType("key7", 10));
nth_element(v.begin(), v.begin() + 3, v.end(),
[](ValueType const& x, ValueType const& y) -> bool
{
// sort descending by value
return y.second < x.second;
});
// print out the top three elements
for (size_t i = 0; i < 3; ++i)
cout << v[i].first << ": " << v[i].second << endl;
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cassert>
#include <iterator>
using namespace std;
class MyMap
{
public:
MyMap(){};
void addValue(string key, int value)
{
_map[key] = value;
_vec.push_back(make_pair(key, value));
sort(_vec.begin(), _vec.end(), Cmp());
}
vector<pair<string, int> > getTop(int n)
{
int len = min((unsigned int)n, _vec.size());
vector<Pair> res;
copy(_vec.begin(), _vec.begin() + len, back_inserter(res));
return res;
}
private:
typedef map<string, int> StrIntMap;
typedef vector<pair<string, int> > PairVector;
typedef pair<string, int> Pair;
StrIntMap _map;
PairVector _vec;
struct Cmp:
public binary_function<const Pair&, const Pair&, bool>
{
bool operator()(const Pair& left, const Pair& right)
{
return right.second < left.second;
}
};
};
int main()
{
MyMap mymap;
mymap.addValue("key1", 10);
mymap.addValue("key2", 3);
mymap.addValue("key3", 230);
mymap.addValue("key4", 15);
mymap.addValue("key6", 66);
mymap.addValue("key7", 10);
auto res = mymap.getTop(3);
for_each(res.begin(), res.end(), [](const pair<string, int> value)
{cout<<value.first<<" "<<value.second<<endl;});
}
The simplest solution would be to use std::transform to build
a second map:
typedef std::map<int, std::string> SortedByValue;
SortedByValue map2;
std::transform(
mymap.begin(), mymap.end(),
std::inserter( map2, map2.end() ),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
Then pick off the last n elements of map2.
Alternatively (and probably more efficient), you could use an
std::vector<std::pair<int, std::string>> and sort it
afterwards:
std::vector<std::pair<int, std::string>> map2( mymap.size() );
std::transform(
mymap.begin(), mymap.end()
map2.begin(),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
std::sort( map2.begin(), map2.end() );
(Note that these solutions optimize for time, at the cost of
more memory.)