How to sort elements of pairs inside vector? - c++

i have a pair of int and string inside a vector, how to sort them first on basis of int and if int value is duplicate then sort according to lexicographical manner of string.
vector< pair<int, string> > v;

You just:
std::sort(v.begin(), v.end());
std::pair is lexicographically compared.
On the other hand if you want to sort them with respect the second element of the std::pair then you would have to defind a custom comparator in the following manner:
std::sort(v.begin(), v.end(), [](std::pair<int, std::string> const &p1,
std::pair<int, std::string> const &p2) {
return (p1.second == p2.second)?
p1.first < p2.first :
p1.second < p2.second;
});

what i did was to store int value multiplied with -1 and then sorted it in ascending order and then again multiplied to -1 to the stored int value to restore int values. doing this made the vector of pairs arranged as needed.

Related

Sorting a vector by unordered map of the elements pointers as keys

I have a vector of elements std::vector<T> my_vec. At some point in my code I assign a score for each element of the vector using an unordered map. After that, I would like to sort the vector by the scores of its elements with the minimum code possible.
I came up with this solution, define the map as follows: std::unordered_map<const T*, float> scores_map. For score assignment, insert the score to the map as follows:
for (const auto& el : my_vec)
scores_map[&el] = calc_score(el);
Then I sort using:
std::sort(my_vec.begin(), my_vec.end(),
[&my_map](const auto& a, const auto& b){return my_map[&a] > my_map[&b];});
Is this considered a bug-free and good practice, if not any idea how to make it so?
#fas wrote in a comment:
Elements in vector are moved during sort, so their pointers also change and scores_map becomes invalid, isn't it?
That is correct. You should not use pointers as keys in scores_map.
Option 1
If the vector contains unique items, you may use the T as the key type.
for (const auto& el : my_vec)
scores_map[el] = calc_score(el);
Then sort using:
std::sort(my_vec.begin(), my_vec.end(),
[&my_map](const auto& a, const auto& b){return my_map[a] > my_map[b];});
Option 2
If the vector does not contain unique elements, you may use the following strategy.
Use indices as the key of my_map.
Create a helper std::vector<size_t> object that contains just indices.
Sort the vector of indices.
Use the sorted indices vector to fetch the elements from my_vec.
for (size_t i = 0; i < my_vec.size(); ++i )
scores_map[i] = calc_score(my_vec[i]);
// Create the vector of indices
std::vector<size_t> indices_vec(my_vec.size());
for ( size_t i = 0; i < indices_vec.size(); ++i )
{
indices_vec[i] = i;
}
// Sort the vector of indices
std::sort(indices_vec.begin(), indices_vec.end(),
[&my_map](size_t a, size_t b){return my_map[a] > my_map[b];});
for (auto index : indices_vec)
{
// Use my_vec[index]
}
No, this not bug-free. std::sort will change the addresses of the elements.
You could store the score with each element in a pair:
std::pair<float, T>
and sort the vector
std::vector<std::pair<float, T> > my_vec
with
std::sort(my_vec.begin(), my_vec.end(),
[](const auto& a, const auto& b){return a.first > b.first;});

Erase element by value from vector of pairs

Since C++20, we can erase an element by value from a vector by doing, for example:
std::vector<int> v = {10,20,30,40,50};
std::erase(v,30);
That's really convenient and all not to mention there's also std::erase_if.
However, what if we have a vector of pairs and we want to erase, only if the second value of the pair matches?
std::pair<int, std::string> foo = std::make_pair(1,"1");
std::pair<int, std::string> foo2 = std::make_pair(2,"2");
std::vector< std::pair<int, std::string> > v;
v.push_back(foo);
v.push_back(foo2);
std::erase(v, make_pair(1,"2")); //This is not going to work!
So, is there a way to erase the element by the second value from a vector of pairs?
It would be something like:
std::erase_if(v, [](const auto& p){ return p.second == "2"; });
Demo

max_element on vector of pairs without "predicate"

I want to find the max element of vector of pairs.
My criteria is: the max element is one with highest second value of the pair.
I did this:
auto max_angle = std::max_element(begin(angles), end(angles),
[](const std::pair<int, int>& left, const std::pair<int, int>& right){
return left.second < right.second;
});
Is it possible to do it without writing a predicate? Is there any easier way for pairs since it is a std struct?
No you can't, because by default std::pairs are compared lexicographically, meaning element-wise left to right. As such, your solution is the simplest solution you can have.

find the vector with the largest size among group of vectors c++

Given a vector of vectors, I want to find the vector with the laregest size, and I use the following code:
bool Longest(vector<int> &A, vector<int> &B){
return A.size()>B.size();
}
vector<vector<int> >::iterator max_itr= max_element(L.begin(),L.end(),Longest);
where L is a vector of vectors (vector<vector<int> >)
I keep getting the iterator point to the L.begin(). Any suggestions?
The comparison functor object passed to std::max_element should return true if the first operand is less than the second one. Your comparison has this the wrong way around. You need
bool Longest(const vector<int> &A, const vector<int> &B)
{
return A.size() < B.size();
}
Also note that it is better for the parameters to be const references because the comparison operation should not modify its operands.
Here's a working example.

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;