Search in vector<std::pair<int, vector<int> > > - c++

I would like to search within a vector<std::pair<int, vector<int> > >. This won't work due to the vector parameters:
std::vector<std::pair<int, std::vector<int> > > myVec;
iterator = find(myVec.begin(), myVec.end(), i);
Search would be on the first std::pair template parameter (int), in order to get the vector associated with it.

std::vector<std::pair<int, std::vector<int> > > myVec;
This requires C++0x for the lambda expression:
typedef std::pair<int, std::vector<int>> pair_type
std::find_if(myVec.begin(), myVec.end(), [i](pair_type const& pair)
{ return pair.first == i; });
If you're not using C++0x then either roll out your own loop or use something like Boost.Phoenix/Boost.Lambda.
Or, for both cases, why not use std::map?

You could make do with the following (pretty ugly) functoid:
struct FindFirst {
FindFirst(int i) : toFind(i) { }
int toFind;
bool operator()
( const std::pair<int, std::vector<int> > &p ) {
return p.first==toFind;
}
};
using it like this ( I couldn't get bind2nd to work - that's why I used the c'tor ):
int valueToFind = 4;
std::find_if(myVec.begin(), myVec.end(), FindFirst(valueToFind));
I think what you would like is a map:
std::map< int, vector< int > > foo;
You can then add elements, search by key etc:
int key = 4; //This will be the key
vector<int> value(5, 4); //Fill some values (5 4's in this case) into the vector
foo[key]=value; //Adds the pair to the map. Alternatively;
foo.insert( make_pair(key, value) ); //Does the same thing (in this context)
Looking at the way you've done things though, you might be wanting a std::multimap (which allows multiple values to have the same key) Class docs here

You're trying to map an int to a vector of int.
So try map<int, vector<int> >.

The second template parameter of a vector is the allocator - your compiler can probably puzzle out what you wanted to say, the declaration is wrong anyway. What you probably want is some sort of map type, like iammilind suggested.

Related

Push_back map into vector

How can I push_back a std::map element into std::vector?
std::vector<std::map<int, int>> v;
// Error
v.push_back(std::make_pair(0, 1))
What cause this error?
Using
v.push_back(std::make_pair(0, 1))
is a problem since a std::pair cannot be converted to a std::map. You can resolve the problem using one of the following methods that I can think of.
Method 1
If you can use a C++11 compiler,
v.push_back({{0, 1}});
Method 2
std::map<int, int> m;
m.insert(std::make_pair(0, 1));
v.push_back(m);
Method 3
std::map<int, int> m;
v.push_back(m);
v.back().insert(std::make_pair(0, 1));
std::vector<std::map<int, int>> v;
means you are declaring vector of maps!
push_back a map into vector, like this
v.push_back({std::make_pair(0, 1)}); //Needs C++11
OR
std::map<int, int> map1;
map1.insert(std::make_pair(0, 1));
v.push_back(map1);
push_back'ng a pair will obviously result into a compilation error.
You can push a map into your Data Structure, std::vector<std::map<int, int>> v. not a pair i.e. element of map.
So, do as following.
std::map<int,int> t;
t.insert(std::make_pair(0, 1));
v.push_back(t);
or in case of C++11
v.push_back({{1,2}});
Or alternatively you can go for emplace_back too.
void emplace_work_around(
std::vector<std::map<int, int>>& v,
std::initializer_list<std::pair<const int,int>> item
)
{
v.emplace_back(item);
}
int main()
{
std::vector<std::map<int, int>> v;
emplace_work_around(v,{{1,2}});
}

How to sort a set of pairs?

How shall I use the qsort function to sort a set of pairs ?
This is my set :
set< pair< int, int> > my_set
This I guess should be my compare function:
int compare (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
Should my qsort look like this?
qsort (my_set, no_of_pairs, sizeof(int), compare);
When I sort them I want to sort by the values of a bi-dimensional array **M;, where
M[my_set.first][my_set.second] = the_value
First, prefer std::sort to qsort with c++ std containers.
Secondly, you cannot sort a std::set a posteriori. std::set is sorted.
You can however specify a custom sorting for the std::set at instanciation using a 2nd template parameter.
Refer to the specs.
What you could do, if you need to sort the data after the fact, is use a std::vector instead. There is an algorithm that will cull the duplicate value.
This proposed solution assumes M is some global variable.
#include <algorithm>
bool less_than(const std::pair<int, int> &some, const std::pair<int, int> &other) {
return M[some.first][some.second] < M[other.first][other.second];
}
std::sort(yourvector.begin(), yourvector.end(), less_than);
If M isn't a global variable, you could do something like that :
struc Compair { // see what I did there ? #sofunny
bool operator() (const std::pair<int, int> &some, const std::pair<int, int> &other) {
return M[some.first][some.second] < M[other.first][other.second];
}
int **M;
}
then in main :
Compair mycomparefunctor;
mycomparefunctor.M = arr; // arr is the original int **
std::sort(yourvector.begin(), yourvector.end(), mycomparefunctor);
EDIT :
If you must use a std::set then you should define the custom ordering when you declare it, like so :
Compair mypredicate;
mypredicate.M = arr; // arr is the original int **
std::set<std::pair<int, int>, mypredicate> myset;
// add stuff to the set. They will be sorted following your predicate.
Be careful though : in a set you cannot add 2 items that compare equal. So if your int ** 2D array has several values that are equal, you won't be able to have several pairs corresponding to indexes of equal value in the set.
You're going about this fairly wrong.
Let's assume that we know the maximum value for each member of the pair. If you don't know this, then you need to figure it out. I'm going to assume that it is 100.
Then all we need to do is iterate over the set, and insert them into the new array. There's no faster way to go about this.
int M[100][100] = {};
for (auto const & pair : my_set)
M[pair.first][pair.second] = the_value;

C++ maps, vectors, pairs with references

I am trying to share references between a map and another vector as in the following code:
template <typename T>
void f(T& x) {
std::map<T, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair(kv.first, 0) );
}
}
and call this function from another
int x = 2;
f(x);
But this does not compile.
error: no matching function for call to ‘std::vector<std::pair<int&, int>, std::allocator<std::pair<int&, int> > >::push_back(std::pair<int, int>)’
v1.push_back( make_pair(kv.first, 0) );
Does anybody see why?
EDIT
I was able to make things work with
std::reference_wrapper
Here is my current solution.
template <typename T>
void f(T& x) {
std::map<std::reference_wrapper<T>, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair( std::ref(kv.first) , 0) );
}
v1[0].first = 34;
}
And
int x = 2;
f(x);
cout << x << endl;
prints 34. Do people see any cons with this vs the pointer based approach?
In general you can store std::pair<T&,int> in an std::vector, but you have to insert values correctly.
make_pair(x, 0) creates a std::pair<T,int> with no reference.
You can for example use an initializer lists instead:
v.push_back({x,0});
Another method would be using std::ref:
v.push_back(std::make_pair(std::ref(x),0));
Example:
std::vector<std::pair<int&,int>> v;
int a = 1;
v.push_back({a,a});
v.push_back(std::make_pair(std::ref(a),a));
a = 2;
std::cout << v[0].first << " " << v[0].second << std::endl; // will write "2 1"
However for your purpose it will not work. You should not get references to keys of a std::map. A std::map is a sorted container, you can not just change a key from the outside. This would mess up the inner workings of the std::map.
Because of type mismatch:
vector< pair<T&, int> > v1; // value_type is pair<int&, int>
// ^^^^^^^^^^^^^
and
v1.push_back( make_pair(kv.first, 0) );
// ^^^^^^^^^^^^^^^^^^^^^^ pair<int, int>
A lvalue-reference to int cannot be initialized with an rvalue.
You cannot have a container of references.
A reference must be initialized at the point of declaration, and declaring a container of references implies filling that container later on.
You could use pointers instead. Non (memory) owning pointers are considered valid c++ style.
Second option would be to look into
std::reference_wrapper
If you really want to reference the same object in your map and your vector of pairs, then you'd be better off looking at a pointer based solution. If your original type is stack based then your secondary collection could use a raw pointer - since it does not have ownership of the key object and so should not take any role in retaining or destructing it - to deference it. Although you'll have to be careful about managing the two collections in sync to avoid any dangling pointers.
That said, depending on why you want to do it, if it's a simple int it may not be worth the complexity.

C++ Copy a vector of pair<int,int> to a vector<int>

I've a vector of pair which I need to copy them linearly to a vector of ints. I've the following code which works well, but I'm not sure if it's safe considering struct padding issues in C++.
std::vector < std::pair<int, int> > test_vector;
for (int i=0;i<5;i++) {
test_vector.push_back(std::make_pair(i,i*5));
}
std::vector<int> int_vec(test_vector.size() * 2);
std::copy(reinterpret_cast<int*>(&(*test_vector.begin())),reinterpret_cast<int*>(&(*test_vector.end())),int_vec.begin());
Now, my question is - Is the above code safe? If not, is there an elegant way to do it without writing a loop?
How about std::transform and a lambda function ?
std::vector<int> v;
std::transform(test_vector.begin(), test_vector.end(), std::back_inserter(v),
[&v](const std::pair<int, int> &p)
{ v.push_back( p.first);
return p.second ;});
If you can't use C++11, and probably "hate" doing linear copying using loops
You can use functor like:
struct X{
X(std::vector<int> &x) :v(x){}
int operator () (const std::pair<int, int> &p)
{
v.push_back(p.first);
return p.second;
}
std::vector<int> &v;
};
std::vector<int> v; //Final vector
std::transform(test_vector.begin(),
test_vector.end(),
std::back_inserter(v),
X(v));
std::vector<int> ::iterator it;
for(it=v.begin() ; it!=v.end() ;++it)
std::cout<<*it<<" ";
You don't need any fanciness for this problem. A simple for loop will do, especially if you can't use C++11
std::vector < std::pair<int, int> > test_vector;
std::vector<int> int_vec; int_vec.reserve(test_vector.size() * 2);
for (std::vector < std::pair<int, int> >::const_iterator it = test_vector.begin(), end_it = test_vector.end(); it != end_it; ++it)
{
int_vec.push_back(it->first);
int_vec.push_back(it->second);
}
You're right to be concerned about structure padding issues, but I think you haven't really faced the central assumption that your code is making:
Can I treat a std::pair<int, int> as an array of two integers, with the .first being the first element in the array and .second being the second element?
From a "correctness" point of view, I'd say "no". You've identified padding issues, but there's also the ordering of the fields. There's really no guarantee that .first has a lower memory address than .second.
From a "practical" point of view, I'd be quite surprised your that code did not work. [ Edit: Neil has pointed out a concrete example where there are padding issues; so color me surprised. Besides being "bad form", I now consider the code broken in practice. ]
As for a solution, you can use for_each with a custom action that pushes both elements of the pair (untested code)
struct action {
action ( vector<int> & target ) : t_(target) {}
void operator () ( const pair &p ) const
{ t_.push_back(p.first); t_.push_back(p.second); }
private:
vector<int> &t_;
}
for_each ( test_vector.begin(), test_vector.end(), action(v));
A reinterpret_cast is usually bad news. Wouldn't you be better off reserve()ing enough space in the destination vector and then calling std::for_each on the source vector of pairs and then have the function/lambda push_back both first and second into the destination vector ?

how copy the first elements of pair in vector to another vector?

I have a std::vector with element of the type std::pair. With some algorithm, I return two iterators (range) so I would like to pick up all elements within that range and copy the first entry of the pair to another vector
std::vector< pair<double, int> > data;
std::vector<double> data2;
std::vector< pair<double, int> >::iterator it1, it2;
for (;it1!=it2; it1++)
{
data2.push_back(it1->first);
}
Using a loop will do that but I wonder if there is a straightforward stl algorithm to do that. Since the data size if pretty big and above operation will be repeated for many times, using a loop is pretty slow.
If you are after an algorithm to do this for you, you can use the four parameter overload of std::transform:
#include <algorithm> // for transform
#include <iterator> // for back_inserted and distance
....
std::vector< pair<double, int> > data;
std::vector<double> data2;
data2.reserve(std::distance(it1, it2));
std::transform(it1,
it2,
std::back_inserter(data2),
[](const std::pair<double, int>& p){return p.first;});
If you don't have C++11 support, you can use a function instead of the lambda expression:
double foo(const std::pair<double, int>& p) { return p.first; }
std::transform(it1,
it2,
std::back_inserter(data2),
foo);
It's because of the operator precedence. The select operator . has higher precedence than the dereference operator *.
So what the compiler thinks you're writing is
*(it1.first)
when you mean
(*it1).first