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

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

Related

How to remove duplicates from a vector of pair<int, Object>

This is what I am trying right now. I made a comparison function:
bool compare(const std::pair<int, Object>& left, const std::pair<int, Object>& right)
{
return (left.second.name == right.second.name) && (left.second.time == right.second.time) &&
(left.second.value == right.second.value);
}
After I add an element I call std::unique to filter duplicates:
data.push_back(std::make_pair(index, obj));
data.erase(std::unique(data.begin(), data.end(), compare), data.end());
But it seems that this doesn't work. And I don't know what the problem is.
From my understanding std::unique should use the compare predicate.
How should I update my code to make this work ?
I am using C++03.
edit:
I have tried to sort it too, but still doens't work.
bool compare2(const std::pair<int, Object>& left, const std::pair<int, Object>& right)
{
return (left.second.time< right.second.time);
}
std::sort(simulatedLatchData.begin(), simulatedLatchData.end(), compare2);
std::unique requires the range passed to it to have all the duplicate elements next to one another in order to work.
You can use std::sort on the range before you a call unique to achieve that as sorting automatically groups duplicates.
Sorting and filtering is nice, but since you never want any duplicate, why not use std::set?
And while we're at it, these pairs look suspiciously like key-values, so how about std::map?
If you want to keep only unique objects, then use an appropriate container type, such as a std::set (or std::map). For example
bool operator<(object const&, object const&);
std::set<object> data;
object obj = new_object(/*...*/);
data.insert(obj); // will only insert if unique

c++ improve vector sorting by presorting with old vector

I have a a vector of pair with the following typdef
typedef std::pair<double, int> myPairType;
typedef std::vector<myPairType> myVectorType;
myVectorType myVector;
I fill this vector with double values and the int part of the pair is an index.
The vector then looks like this
0.6594 1
0.5434 2
0.5245 3
0.8431 4
...
My program has a number of time steps with slight variations in the double values and every time step I sort this vector with std::sort to something like this.
0.5245 3
0.5434 2
0.6594 1
0.8431 4
The idea is now to somehow use the vector from the last time step (the "old vector, already sorted) to presort the current vector (the new vector, not yet sorted). And use an insertions sort or tim sort to sort the "rest" of the then presorted vector.
Is this somehow possible? I couldn't find a function to order the "new" vector of pairs by one part (the int part).
And if it is possible could this be faster then sorting the whole unsorted "new" vector?
Thanks for any pointers into the right direction.
tiom
UPDATE
First of all thanks for all the suggestions and code examples. I will have a look at each of them and do some benchmarking if they will speed up the process.
Since there where some questions regarding the vectors I will try to explain in more detail what I want to accomplish.
As I said I have a number if time steps 1 to n. For every time step I have a vector of double data values with approximately 260000 elements.
In every time step I add an index to this vector which will result in a vector of pairs <double, int>. See the following code snippet.
typedef typename myVectorType::iterator myVectorTypeIterator; // iterator for myVector
std::vector<double> vectorData; // holds the double data values
myVectorType myVector(vectorData.size()); // vector of pairs <double, int>
myVectorTypeIterator myVectorIter = myVector.begin();
// generating of the index
for (int i = 0; i < vectorData.size(); ++i) {
myVectorIter->first = vectorData[i];
myVectorIter->second = i;
++myVectorIter;
}
std::sort(myVector.begin(), myVector.end() );
(The index is 0 based. Sorry for my initial mistake in the example above)
I do this for every time step and then sort this vector of pairs with std::sort.
The idea was now to use the sorted vector of pairs of time step j-1 (lets call it vectorOld) in time step j as a "presorter" for the "new" myVector since I assume the ordering of the sorted "new" myVector of time step j will only differ in some cases from the already sorted vectorOld of time step j-1.
With "presorter" I mean to rearrange the pairs in the "new" myVector into a vector presortedVector of type myVectorType by the same index order as the vectorOld and then let a tim sort or some similar sorting algorithm that is good in presorted date do the rest of the sorting.
Some data examples:
This is what the beginning of myVector looks like in time step j-1 before the sorting.
0.0688015 0
0.0832928 1
0.0482259 2
0.142874 3
0.314859 4
0.332909 5
...
And after the sorting
0.000102207 23836
0.000107378 256594
0.00010781 51300
0.000109315 95454
0.000109792 102172
...
So I in the next time step j this is my vectorOld and I like to take the element with index 23836 of the "new" myVector and put it in the first place of the presortedVector, element with index 256594 should be the second element in presortedVector and so on. But the elements have to keep their original index. So 256594 will not be index 0 but only element 0 in presortedVector still with index 256594
I hope this is a better explanation of my plan.
First, scan through the sequence to find the first element that's smaller than the preceding one (either a loop, or C++11's std::is_sorted_until). This is the start of the unsorted portion. Use std::sort on the remainder, then merge the two halves with std::inplace_merge.
template<class RandomIt, class Compare>
void sort_new_elements(RandomIt first, RandomIt last, Compare comp)
{
RandomIt mid = std::is_sorted_until(first, last, comp);
std::sort(mid, last, comp);
std::inplace_merge(first, mid, last, comp);
}
This should be more efficient than sorting the whole sequence indiscriminately, as long as the presorted sequence at the front is significantly larger than the unsorted part.
Using the sorted vector would likely result in more comparisons (just to find a matching item).
What you seem to be looking for is a self-ordering container.
You could use a set (and remove/re-insert on modification).
Alternatively you could use Boost Multi Index which affords a bit more convenience (e.g. use a struct instead of the pair)
I have no idea if this could be faster than sorting the whole unsorted "new" vector. It will depend on the data.
But this will create a sorted copy of a new vector based on the order of an old vector:
myVectorType getSorted(const myVectorType& unsorted, const myVectorType& old) {
myVectorType sorted(unsorted.size());
auto matching_value
= [&unsorted](const myPairType& value)
{ return unsorted[value.second - 1]; };
std::transform(old.begin(), old.end(), sorted.begin(), matching_value);
return sorted;
}
You will then need to "finish" sorting this vector. I don't know how much quicker (if at all) this will be than sorting it from scratch.
Live demo.
Well you can create new vector with the order of the old and then use algorithms that has good complexity for (nearly) sorted inputs for the restoration of order.
Below I put an example of how it works, with Mark's function as restore_order:
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
using namespace std;
typedef std::pair<double, int> myPairType;
typedef std::vector<myPairType> myVectorType;
void outputMV(const myVectorType& vect, std::ostream& out)
{
for(const auto& element : vect)
out << element.first << " " << element.second << '\n';
}
//https://stackoverflow.com/a/28813905/1133179
template<class RandomIt, class Compare>
void restore_order(RandomIt first, RandomIt last, Compare comp)
{
RandomIt mid = std::is_sorted_until(first, last, comp);
std::sort(mid, last, comp);
std::inplace_merge(first, mid, last, comp);
}
int main() {
myVectorType myVector = {{3.5,0},{1.4,1},{2.5,2},{1.0,3}};
myVectorType mv2 = {{3.6,0},{1.35,1},{2.6,2},{1.36,3}};
auto comparer = [] (const auto& lhs, const auto& rhs) { return lhs.first < rhs.first;};
// make sure we didn't mess with the initial indexing
int i = 0;
for(auto& element : myVector) element.second = i++;
i = 0;
for(auto& element : mv2) element.second = i++;
//sort the initial vector
std::sort(myVector.begin(), myVector.end(), comparer);
outputMV(myVector, cout);
// this will replace each element of myVector with a corresponding
// value from mv2 using the old sorted order
std::for_each(myVector.begin(), myVector.end(),
[mv2] (auto& el) {el = mv2[el.second];}
);
// restore order in case it was different for the new vector
restore_order(myVector.begin(), myVector.end(), comparer);
outputMV(myVector, cout);
return 0;
}
This works in O(n) up to the point of restore then. Then the trick is to use good function for it. A nice candidate will have good complexity for nearly sorted inputs. I used function that Mark Ransom posted, which works, but still isn't perfect.
It could get outperformed by bubble sort inspired method. Something like, iterate over each element, if the order between current and next element is wrong recursively swap current and next. However there is a bet on how much the order changes - if the order doesn't vary much you will stay close to O(2n), if does - you will go up to O(n^2).
I think the best would be an implementation of natural merge sort. That has best case (sorted input) O(n), and worst O(n log n).

How can I sort a std::map first by value, then by key?

I need to sort a std::map by value, then by key. The map contains data like the following:
1 realistically
8 really
4 reason
3 reasonable
1 reasonably
1 reassemble
1 reassembled
2 recognize
92 record
48 records
7 recs
I need to get the values in order, but the kicker is that the keys need to be in alphabetical order after the values are in order. How can I do this?
std::map will sort its elements by keys. It doesn't care about the values when sorting.
You can use std::vector<std::pair<K,V>> then sort it using std::sort followed by std::stable_sort:
std::vector<std::pair<K,V>> items;
//fill items
//sort by value using std::sort
std::sort(items.begin(), items.end(), value_comparer);
//sort by key using std::stable_sort
std::stable_sort(items.begin(), items.end(), key_comparer);
The first sort should use std::sort since it is nlog(n), and then use std::stable_sort which is n(log(n))^2 in the worst case.
Note that while std::sort is chosen for performance reason, std::stable_sort is needed for correct ordering, as you want the order-by-value to be preserved.
#gsf noted in the comment, you could use only std::sort if you choose a comparer which compares values first, and IF they're equal, sort the keys.
auto cmp = [](std::pair<K,V> const & a, std::pair<K,V> const & b)
{
return a.second != b.second? a.second < b.second : a.first < b.first;
};
std::sort(items.begin(), items.end(), cmp);
That should be efficient.
But wait, there is a better approach: store std::pair<V,K> instead of std::pair<K,V> and then you don't need any comparer at all — the standard comparer for std::pair would be enough, as it compares first (which is V) first then second which is K:
std::vector<std::pair<V,K>> items;
//...
std::sort(items.begin(), items.end());
That should work great.
You can use std::set instead of std::map.
You can store both key and value in std::pair and the type of container will look like this:
std::set< std::pair<int, std::string> > items;
std::set will sort it's values both by original keys and values that were stored in std::map.
As explained in Nawaz's answer, you cannot sort your map by itself as you need it, because std::map sorts its elements based on the keys only. So, you need a different container, but if you have to stick to your map, then you can still copy its content (temporarily) into another data structure.
I think, the best solution is to use a std::set storing flipped key-value pairs as presented in ks1322's answer.
The std::set is sorted by default and the order of the pairs is exactly as you need it:
3) If lhs.first<rhs.first, returns true. Otherwise, if rhs.first<lhs.first, returns false. Otherwise, if lhs.second<rhs.second, returns true. Otherwise, returns false.
This way you don't need an additional sorting step and the resulting code is quite short:
std::map<std::string, int> m; // Your original map.
m["realistically"] = 1;
m["really"] = 8;
m["reason"] = 4;
m["reasonable"] = 3;
m["reasonably"] = 1;
m["reassemble"] = 1;
m["reassembled"] = 1;
m["recognize"] = 2;
m["record"] = 92;
m["records"] = 48;
m["recs"] = 7;
std::set<std::pair<int, std::string>> s; // The new (temporary) container.
for (auto const &kv : m)
s.emplace(kv.second, kv.first); // Flip the pairs.
for (auto const &vk : s)
std::cout << std::setw(3) << vk.first << std::setw(15) << vk.second << std::endl;
Output:
1 realistically
1 reasonably
1 reassemble
1 reassembled
2 recognize
3 reasonable
4 reason
7 recs
8 really
48 records
92 record
Code on Ideone
Note: Since C++17 you can use range-based for loops together with structured bindings for iterating over a map.
As a result, the code for copying your map becomes even shorter and more readable:
for (auto const &[k, v] : m)
s.emplace(v, k); // Flip the pairs.
std::map already sorts the values using a predicate you define or std::less if you don't provide one. std::set will also store items in order of the of a define comparator. However neither set nor map allow you to have multiple keys. I would suggest defining a std::map<int,std::set<string> if you want to accomplish this using your data structure alone. You should also realize that std::less for string will sort lexicographically not alphabetically.
EDIT: The other two answers make a good point. I'm assuming that you want to order them into some other structure, or in order to print them out.
"Best" can mean a number of different things. Do you mean "easiest," "fastest," "most efficient," "least code," "most readable?"
The most obvious approach is to loop through twice. On the first pass, order the values:
if(current_value > examined_value)
{
current_value = examined_value
(and then swap them, however you like)
}
Then on the second pass, alphabetize the words, but only if their values match.
if(current_value == examined_value)
{
(alphabetize the two)
}
Strictly speaking, this is a "bubble sort" which is slow because every time you make a swap, you have to start over. One "pass" is finished when you get through the whole list without making any swaps.
There are other sorting algorithms, but the principle would be the same: order by value, then alphabetize.

sort operator not working in C++

I'm having trouble using my sort operator since I need to sort only the first element in the pair. The code is simple but is not working:
The operator is defined in:
struct sort_pred {
bool operator()(const CromosomaIndex &left, const CromosomaIndex &right) {
return left.first < right.first;
}
};
and the type is
typedef std::pair<double,int> CromosomaIndex;
I'm trying to sort the array like this:
CromosomaIndex nuevo[2];
nuevo[0].first = 0.01;
nuevo[0].second = 0;
nuevo[1].first = 0.009;
nuevo[1].second = 1;
int elements = sizeof(nuevo) / sizeof(nuevo[0]);
sort(nuevo, nuevo+ elements, sort_pred());
But the problem is that this is sorting the first and the second element and I only want to sort the first element and keep the second fixed.
Any thoughts?
If you want the results to depend on the original order, use std::stable_sort.
This approach sorts pairs as a single unit, which is what it is expected to do: it never make sense to break up the first and the second of the pair. If you would like to sort only the first item and leave the second in place, you will end up with a different set of pairs.
If you want to sort the first separately from the second, place them in separate arrays (better yet, use vectors) and sort the first vector. Then iterate both vectors, and make a new set of pairs.
I am not sure that you understood the answer to the other question, but you do want the whole pair to be reordered according to the double value. The original index (the int) must be attached to the double that was in that location in the original vector so that you can recover the location. Note that if you sorted only the double within the pair, then the value of the int would be the location in the array... which does not need to be maintained as a datum at all.
Alternatively, you can consider a similar (although slightly different) solution. Create a single vector of integers that is initialized with values in the range [0..N) where N is the size of the vector of doubles. Then sort the vector of indices using a comparator functor that instead of looking at the value (int) passed in will check the value in the original double vector:
struct dereference_cmp {
std::vector<double> const & d_data;
dereference_cmp( std::vector<double> const & data ) : d_data(data) {}
bool operator()( int lhs, int rhs ) const {
return d_data[lhs] < d_data[rhs];
}
};
std::vector<double> d = ...;
std::vector<int> ints;
ints.reserve( d.size() );
for ( int i = 0; i < d.size(); ++i ) ints.push_back(i);
std::sort( d.begin(), d.end(), dereference_cmp(d) );
In this approach, note that what is not being reordered are the doubles, but rather the vector of indices. After the sort completes the vector of indices will contain locations into the vector of double such that i < j => d[ ints[i] ] <= d[ ints[j] ].
Note that in the whole process, what you want to reorder is the indices (in the original approach to be able to reconstruct the unsorted vector, in this approach to be able to find the values in sorted order), and the original vector is there only to provide the criterion for the sort.
Also note that the only reason to sort only the indices and not a modified container with both the value and the index would be if the cost of moving the data was high (say that each datum is a large object that cannot be cheaply moved, as a struct holding an array --not vector-- of data).

vector sort and erase won't work

When using this code to remove duplicates I get invalid operands to binary expression errors. I think that this is down to using a vector of a struct but I am not sure I have Googled my question and I get this code over and over again which suggests that this code is right but it isn't working for me.
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
Any help will be appreciated.
EDIT:
fileSize = textFile.size();
vector<wordFrequency> words (fileSize);
int index = 0;
for(int i = 0; i <= fileSize - 1; i++)
{
for(int j = 0; j < fileSize - 1; j++)
{
if(string::npos != textFile[i].find(textFile[j]))
{
words[i].Word = textFile[i];
words[i].Times = index++;
}
}
index = 0;
}
sort(words.begin(), words.end());
words.erase(unique(words.begin(), words.end(), words.end()));
First problem.
unique used wrongly
unique(words.begin(), words.end(), words.end()));
You are calling the three operand form of unique, which takes a start, an end, and a predicate. The compiler will pass words.end() as the predicate, and the function expects that to be your comparison functor. Obviously, it isn't one, and you enter the happy world of C++ error messages.
Second problem.
either use the predicate form or define an ordering
See the definitions of sort and unique.
You can either provide a
bool operator< (wordFrequency const &lhs, wordFrequency const &rhs)
{
return lhs.val_ < rhs.val_;
}
, but only do this if a less-than operation makes sense for that type, i.e. if there is a natural ordering, and if it's not just arbitrary (maybe you want other sort orders in the future?).
In the general case, use the predicate forms for sorting:
auto pred = [](wordFrequency const &lhs, wordFrequency const &rhs)
{
return lhs.foo < rhs.foo;
};
sort (words.begin(), words.end(), pred);
words.erase (unique (words.begin(), words.end(), pred));
If you can't C++11, write a functor:
struct FreqAscending { // should make it adaptible with std::binary_function
bool operator() (wordFrequency const &lhs, wordFrequency const &rhs) const
{ ... };
};
I guess in your case ("frequency of words"), operator<makes sense.
Also note vector::erase: This will remove the element indicated by the passed iterator. But, see also std::unique, unique returns an iterator to the new end of the range, and I am not sure if you really want to remove the new end of the range. Is this what you mean?
words.erase (words.begin(),
unique (words.begin(), words.end(), pred));
Third problem.
If you only need top ten, don't sort
C++ comes with different sorting algorithms (based on this). For top 10, you can use:
nth_element: gives you the top elements without sorting them
partial_sort: gives you the top elements, sorted
This wastes less watts on your CPU, will contribute to overall desktop performance, and your laptop batteries last longer so can do even more sorts.
The most probable answer is that operator< is not declared for the type of object vec contains. Have you overloaded it? It should look something like that:
bool operator<(const YourType& _a, const YourType& _b)
{
//... comparison check here
}
That code should work, as std::unique returns an iterator pointing to the beginning of the duplicate elements. What type is your vector containing? Perhaps you need to implement the equality operator.