Initializing set with comparator in C++ - c++

I encountered the following code:
#include <iostream>
#include <set>
int main() {
auto comp = [](int x, int y){return (x > y); };
std::set<int, decltype(comp)> inversed({1,2,3,4,5}, comp);
for ( auto i = inversed.begin(); i != inversed.end(); ++i ) {
std::cout << *i << std::endl;
}
return 0;
}
The code prints "5 4 3 2 1", i.e initial set in inversed order. Can anybody explain why? How comparator influences initialization of the set?
Thanks,
Kostya

An std::set uses a comparator to determine the order of elements. The default semantics for a comparator is "less", which means that if running a comparator on two values (A,B) returns true, then A should be placed before B.
In your case, the comparator does the opposite (returns true if A is "greater" than B), that's why bigger elements appear in front of the smaller ones.

When defining comp, you are defining the order function for your set. For your set, two elements will be ordered if its order function will be fulfilled.
So, being std::set an ordered container, you get that result, i.e. inversed store its elements sorted, but the order is descending because it's the order defined by comp.

Related

How can I reverse loop over a map by value?

I need to get the pairs of the map sorted by its values, i wonder if it is posible without an temporal declaration.
I know i can sort it if i make another map with the keys and values swaped, but i am searching for a better solution.
I can't sort the elements afterwards because i only need extract the chars and put them on an array for example.
std::map<char,int> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
for(/* code here */){
std::cout << /* code here */ << std::endl;
}
Desired outout:
J 5
A 4
G 3
V 2
N 1
This cannot be done with std::map.
This template has an optional template argument which allows for a custom sorting, but this sorting can only be done on the map's key:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare.
This choice has been done as to not impose the map's value to be an orderable type.
As an alternative, you can use type std::vector<std::pair<char, int>> in combination with std::sort.
#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::pair<char,int>> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
std::sort(begin(list), end(list), [](auto lhs, auto rhs) {
return lhs.second > rhs.second ? true : ( rhs.first > rhs.first );
});
for(auto const& pair : list) {
std::cout << pair.first << ", " << pair.second << '\n';
}
}
J, 5
A, 4
G, 3
V, 2
N, 1
Live demo
The most appropriate solution depends on HOW are you going to use that list. Is it created once and queried once? Then anything will do: you can just sort the output.
However, if this is a "live" list that is constantly updated, and you need to query it frequently, I would suggest to keep another map of values to vector of keys. Then you would be able to get your result instantly. At a cost
of more expensive insertion, of course.

std::vector<std::pair<int, float>> and void error

I'd like to sort m_correlationValues in descending order and get ids of the sorted list. I've got this error. I'll appreciate your help.
no match for 'operator=' (operand types are 'std::vector<std::pair<int, float> >' and 'void')
return idx_correlation.second; });
void MatrixCanvas::SortMatrix()
{
int naxes = (int) m_correlationData.size();
std::vector<std::pair<int,float>> idx_correlations;
std::vector<std::pair<int,float>> sorted;
std::vector<int> idxs(naxes);
for(int idx =0; idx<naxes;idx++){
idx_correlations[idx] = std::make_pair(idx, m_correlationValues[chosen_row_id][idx]);}
// Wrong
sorted = std::sort(idx_correlations.begin(),
idx_correlations.end(),
[](std::pair<int,float> &idx_correlation){
return idx_correlation.second; });
// this will contain the order:
for(int i =0; i<naxes;i++)
idxs[i] = sorted[i].first;
}
You have two problems:
sort does not return a copy of the sorted range. It modifies the range provided. If you want the original to be left alone, make a copy of it first and then sort it.
std::sort's third argument is a comparator between two values, which has the meaning of "less than". That is, "does a come before b?" For keeping the line short, I replaced your pair<...> type with auto in the lambda, but it'll be deduced to "whatever type of thing" is being sorted.
Note, if you want decreasing, just change < to > in the lambda when it compares the two elements.
Possible fix:
auto sorted = idx_correlations; // full copy
std::sort(sorted.begin(),
sorted.end(),
[](auto const & left, auto const & right) {
return left.first < right.first; });
After that, sorted will be a sorted vector and idx_correlations will be left unchanged. Of course, if you don't mind modifying your original collection, there's no need to make this copy (and you can take begin/end of idx_correlations.
So the main issue I can see in your code, is that you're expecting the std::sort to return the sorted vector, and this is NOT how it works.
https://en.cppreference.com/w/cpp/algorithm/sort
The solution in your case is to get the sorted vector out of the original vector, ie. sorted = idx_correlations then sort the new vector.
sorted = idx_correlations;
std::sort( sorted.begin(), sorted.end(), your_comparator... );
This will do the trick while also maintaining the original vector.
Update: another issue is that your comparator will have TWO arguments not one (two elements to compare for the sort).
The other answers covered proper use of std::sort, I wish to show C++20 std::rannges::sort which have projection functionality what is close to thing you've tried to do:
std::vector<std::pair<int, float>> idx_correlations;
.....
auto sorted = idx_correlations;
std::ranges::sort(sorted, std::greater{}, &std::pair<int, float>::second);
https://godbolt.org/z/4rzzqW9Gx

Use of std::greater

I am curious about the use of std::greater.
When used with sort, it outputs the numbers in descending order. But when used with priority_queue, numbers are output in ascending order. Why so?
Example:
#include <iostream> // std::cout
#include <functional> // std::greater
#include <algorithm> // std::sort
#include <queue> // std::priority_queue
int main () {
int numbers[]={20,40,50,10,30};
std::priority_queue<int, std::vector<int>, std::greater<int>> pq (numbers, numbers+5);
std::sort(numbers, numbers + 5, std::greater<int>());
while(!pq.empty()){
std:: cout << pq.top() << ' ';
pq.pop();
}
std::cout << '\n';
for (int i=0; i<5; i++)
std::cout << numbers[i] << ' ';
return 0;
}
The output of above code is:
10 20 30 40 50
50 40 30 20 10
Or similar lines,
std::priority_queue<int, std::vector<int>, std::greater<int> > creates a min heap whereas std::priority_queue<int, std::vector<int>, std::less<int> > creates a max heap. Could have been the other way round. Why is it so?
Citing std::priority_queue at cppreference [emphasis mine]
A priority queue is a container adaptor that provides constant time
lookup of the largest (by default) element, at the expense of
logarithmic insertion and extraction.
A user-provided Compare can be supplied to change the ordering, e.g.
using std::greater<T> would cause the smallest element to appear as
the top().
So this order is expected, and does not really relate to how std::sort sorts element based on a supplied binary comparison function.
Sorts the elements in the range [first, last) in ascending
order.
...
Parameters
first, last - the range of elements to sort
policy - the execution policy to use. See execution policy for details.
comp - comparison function object (i.e. an object that satisfies the
requirements of Compare) which returns true if the first argument
is less than (i.e. is ordered before) the second.
As std::greater will return true if its first argument is greater than its second one, we expect the elements to be sorted in descending order when using std::sort with std::greater as function object for performing comparisons.
I.e., std::greater just happens to be the function object used for performing comparisons in these two different contexts of your example.

Fail of Vectors Comparison

I'm trying to compare two vectors , and as what I know vectors support the relational operators and it works in this way: compare the 1st element in v1 with 1st element in v2 and so on ..
why the result of the following code is (true) where the last element in v1 > v2 ?!
#include <iostream>
#include <vector>
using namespace std;
void main()
{
vector <int> V1 = { 2,1,0,3 };
vector <int> V2 = { 3,4,2,2 };
cout << (V1 <= V2); //print true !!
system("pause");
}
operator==,!=,<,<=,>,>= compare the contents of both vectors lexicographicall.
From http://en.cppreference.com/w/cpp/algorithm/lexicographical_compare :
Lexicographical comparison is a operation with the following properties:
Two ranges are compared element by element.
The first mismatching element defines which range is lexicographically less or greater than the other.
This is why a string "abcdx" is less than "abced" and [2,1,0,3] is less than [3,4,2,2].
std::vector is a data container and has nothing to do with the mathematical concecpt of a vector beside containing more than one element.
The doc of std::vector states how the comparison works:
The equality
comparison (operator==) is performed by first comparing sizes, and if
they match, the elements are compared sequentially using operator==,
stopping at the first mismatch (as if using algorithm equal).
The less-than comparison (operator<) behaves as if using algorithm
lexicographical_compare, which compares the elements sequentially
using operator< in a reciprocal manner (i.e., checking both a

Sorting using objects in vectors

I have a function that does some computing and set the value returned into my vector.
#include <vector>
#include <algorithm>
#include <iostream>
void VectorLoop(vector<ClassName>& Vector)
{
float TempFloatVariable = 0.0;
int count = 0;
int update = 0;
while (count != Vector.size())
{
if (Vector[count].getValue() == 100.0) //I hardcode this so i can check if the Value is empty or not
{
//code that set all of the variable from the vector's memory's object
TempFloatVariable = AnotherClass.Formula //does the computing and return the value
//code that gets value from object for overloading
//code that gets value from object for overloading
//code that gets value from object for overloading
Vector[count].setValue(TempFloatVariable);
update++;
}
else
{
count++;
}
}
cout << "Computation completed! (" << update << " record(s) were updated)" << endl;
}
After all of the computing is done, I want to sort them from highest to lowest, base on the Value, but I have no idea how to do so, i tried to hard code it, pulling values out manually 1 by 1 to do comparison, but kept failing. And that would defeat the purpose of using vector, there are many examples of sorting using vector, but 90% of it are int values stored in the vectors.
As IgorTandetnik said: you could do
std::sort(Vector.begin(), Vector.end(), [](const PointTwoD& a, const PointTwoD& b)
{
return a.getcivIndex() > b.getcivIndex();
});
which will use the result from the lambda to sort the vector and should do what you want.
To print the objects in the vector you should do something like this:
for(int i = 0; i < Vector.size(); i++)
{
std::cout << Vector[i] << std::endl; //Or whatever your printing function is.
}
Which will iterate over all the objects in the vector and, as they should already be in descending order from the sort, it will print them out in descending order like you wanted.
Edit:
For non C++11 users: you can define a function that does the comparison
bool compare(const PointTwoD& a, const PointTwoD& b)
{
return a.getcivIndex() > b.getcivIndex();
}
and use it instead of the lambda. Like this:
std::sort(Vector.begin(), Vector.end(), compare);
Use std::sort:
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
The first argument is an iterator to the first element you want to sort. The second argument is the final iterator position after sorting (in other words, one past the final element you want to sort). So to sort you would do this:
std::sort(Vector.begin(), Vector.end());
To determine how the sorting is actually performed, you need to define a < operator for PointTwoD that compares the civ index. Alternately you can create an external function that takes two PointTwoDs as parameters and returns a boolean, and specify it through the third argument to std::sort. If the sort function needs to access private members you'll need to declare it as a friend function in the class definition, though.
You can also inline the compare function in the std::sort call using a lambda if you want, like Phantom posted in his answer.