Sorting a Vector of Vector in Cpp - c++

Say I have this vector of vector [[5,10],[2,5],[4,7],[3,9]] and I want to sort it using the sort() method of cpp, such that it becomes this [[5,10],[3,9],[4,7],[2,5]] after sorting. That is I want to sort based on the second index.
Now I have written this code to sort this vector of vector, but it is not working correctly.
static bool compareInterval( vector<vector<int>> &v1, vector<vector<int>> &v2)
{
return (v1[0][1]>v2[0][1]);
}
sort(boxTypes.begin(), boxTypes.end(), compareInterval);
Can anyone tell me where I am going wrong and hwo can I correct it. Thanks in advance.

Your sort could look like
std::sort(boxTypes.begin(), boxTypes.end(), [](auto const& lhs, auto const& rhs) {
return lhs[1] > rhs[1];
});
in other words sorting by the [1] element of each vector and using > to sort in descending order. Note that in the lambda function lhs and rhs are of type const std::vector<int>&.

When your code is sorting vector of vectors then to the boolean function it passes two vectors (not vector of vectors), and compares them to determine if they need to be interchanged, or are they in correct positions relative to each other.
Hence, here you only need to compare 2 vectors (you have tried to compare vector of vectors).
The change you need to make in compareInterval is:
static bool compareInterval( vector<int> &v1, vector<int> &v2)
{
return (v1[1]>v2[1]);
}
Find my testing code below:
#include <bits/stdc++.h>
using namespace std;
static bool compareInterval( vector<int> &v1, vector<int> &v2)
{
return (v1[1]>v2[1]);
}
int main() {
vector<vector<int>> boxTypes = {{5,10},{2,5},{4,7},{3,9}};
sort(boxTypes.begin(), boxTypes.end(), compareInterval);
for(int i=0;i<4;i++)
cout<<boxTypes[i][0]<<" "<<boxTypes[i][1]<<"\n";
}

Range projections will come somewhat handy for this.
ranges::sort algorithm would receive:
just the vector to sort; no iterators to the begin and end.
(optionally) the function you want to use for sorting, greater in this case.
(optionally) the projection: for every element t of the original vector, which happens to be another vector of two elements, get its second element, i.e. t[1], and sort on that.
std::ranges::sort(boxTypes, std::ranges::greater{}, [](auto&& bt) { return bt[1]; });
Note I have only been able to have this compiling on msvc, not on gcc or clang (and with /std:c++latest, not even with /std:c++20; https://godbolt.org/z/9Kqfa9vhx).

Related

How to sort an vector based on any column value by passing the column index as input?

I have already tried the below code but i want to use this same function for sorting based on different columns.
This given code is sorting based on first column only.
bool sortcol( const vector <int> v1, const vector <int> v2)
{
return v1[0]<v2[0];
}
sort(ArrOfTimings.begin(), ArrOfTimings.end(), sortcol);
Is there any way possible to not make multiple functions but making it all work with one only.
Something like this
bool sortcol(int ind, const vector <int> v1, const vector <int> v2)
{
return v1[ind]<v2[ind];
}
sort(ArrOfTimings.begin(), ArrOfTimings.end(), sortcol(0));
You cannot pass additional info to a free function used for comparison for std::sort by means other than global data.
You can create a struct with a member variable storing the column to compare in addition to providing a call operator for comparing the values though.
I'd prefer a lambda, since it results in shorter code though: Simply capture the index.
Note: It's also beneficial to avoid copying the vector by using references as parameters.
void SortByColumn(std::vector<std::vector<int>> & data, size_t column)
{
std::sort(data.begin(), data.end(),
[column](std::vector<int> const& v1, std::vector<int> const& v2)
{
return v1[column] < v2[column];
});
}

Function on multiple vector

I have a sorting algorithm on a vector, and I want to apply it to several vectors, without knowing how much. The only thing I'm sure is that there will be at least 1 vector (always the same) on which I will perform my algorithm. Other will just follow.
Here's an example :
void sort(std::vector<int>& sortVector, std::vector<double>& follow1, std::vector<char>& follow2, ... ){
for (int i = 1; i<vector.size(); ++i){
if ( vector[i-1] > vector[i] ) { //I know it's not sorting here, it's only for the example
std::swap(vector[i-1], vector[i]);
std::swap(follow1[i-1], follow1[i]);
std::swap(follow2[i-1], follow2[i]);
....
}
}
}
I was thinking about using variadic function, but since it's a recursive function, I was wondering if it won't take too much time to everytime create my va_arg list (I'm working on vector sized 500millions/1billions ...). So does something else exists?
As I'm writing this question, I'm understanding that maybe i'm fooling myself, and there is no other way to achieve what I want and variadic function is maybe not that long. (I really don't know, in fact).
EDIT :
In fact, I'm doing an Octree-sorting of datas in order to be usable in opengl.
Since my datas are not always the same (e.g OBJ files will gives me normals, PTS files will gives me Intensity and Colors, ...), I want to be able to reorder all my vectors (in which are contained my datas) so that they have the same order as the position vectors (The vector that contains the positions of my points, it'll be always here).
But all my vectors will have same length, and I want all my followervector to be reorganised as the first one.
If i have 3 Vectors, if I swap first and third values in my first vector, I want to swap first and thrid values in my 2 others vectors.
But my vectors are not all the same. Some will be std::vector<char>, other std::vector<Vec3>, std::vector<unsigned>, and so on.
With range-v3, you may use zip, something like:
template <typename T, typename ... Ranges>
void sort(std::vector<T>& refVector, Ranges&& ... ranges){
ranges::sort(ranges::view::zip(refVector, std::forward<Ranges>(ranges)...));
}
Demo
Or if you don't want to use ranges to compare (for ties in refVector), you can project to use only refVector:
template <typename T, typename ... Ranges>
void sort(std::vector<T>& refVector, Ranges&& ... ranges){
ranges::sort(ranges::view::zip(refVector, std::forward<Ranges>(ranges)...),
std::less<>{},
[](auto& tup) -> T& { return std::get<0>(tup); });
}
Although, I totally agree with the comment of n.m. I suggest to use a vector of vectors which contain the follow vectors and than do a loop over all follow vectors.
void sort(std::vector<int>& vector, std::vector<std::vector<double>>& followers){
for (int i = 1; i<vector.size(); ++i){
if ( vector[i-1] > vector[i] ) {
std::swap(vector[i-1], vector[i]);
for (auto & follow : followers)
std::swap(follow[i-1], follow[i]);
}
}
}
Nevertheless, as n.m. pointed out, perhaps think about putting all your data you like to sort in a class like structure. Than you can have a vector of your class and apply std::sort, see here.
struct MyStruct
{
int key; //content of your int vector named "vector"
double follow1;
std::string follow2;
// all your inforrmation of the follow vectors go here.
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
struct less_than_key
{
inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
};
std::vector < MyStruct > vec;
vec.push_back(MyStruct(4, 1.2, "test"));
vec.push_back(MyStruct(3, 2.8, "a"));
vec.push_back(MyStruct(2, 0.0, "is"));
vec.push_back(MyStruct(1, -10.5, "this"));
std::sort(vec.begin(), vec.end(), less_than_key());
The main problem here is that the std::sort algorithm cannot operate on multiple vectors at the same time.
For the purpose of demonstration, let's assume you have a std::vector<int> v1 and a std::vector<char> v2 (of the same size of course) and you want to sort both depending on the values in v1. To solve this, I basically see three possible solutions, all of which generalize to an arbitrary number of vectors:
1) Put all your data into a single vector.
Define a struct, say Data, that keeps an entry of every data vector.
struct Data
{
int d1;
char d2;
// extend here for more vectors
};
Now construct a new std::vector<Data> and fill it from your original vectors:
std::vector<Data> d(v1.size());
for(std::size_t i = 0; i < d.size(); ++i)
{
d[i].d1 = v1[i];
d[i].d2 = v2[i];
// extend here for more vectors
}
Since everything is stored inside a single vector now, you can use std::sort to bring it into order. Since we want it to be sorted based on the first entry (d1), which stores the values of the first vector, we use a custom predicate:
std::sort(d.begin(), d.end(),
[](const Data& l, const Data& r) { return l.d1 < r.d1; });
Afterwards, all data is sorted in d based on the first vector's values. You can now either work on with the combined vector d or you split the data into the original vectors:
std::transform(d.begin(), d.end(), v1.begin(),
[](const Data& e) { return e.d1; });
std::transform(d.begin(), d.end(), v2.begin(),
[](const Data& e) { return e.d2; });
// extend here for more vectors
2) Use the first vector to compute the indices of the sorted range and use these indices to bring all vectors into order:
First, you attach to all elements in your first vector their current position. Then you sort it using std::sort and a predicate that only compares for the value (ignoring the position).
template<typename T>
std::vector<std::size_t> computeSortIndices(const std::vector<T>& v)
{
std::vector<std::pair<T, std::size_t>> d(v.size());
for(std::size_t i = 0; i < v.size(); ++i)
d[i] = std::make_pair(v[i], i);
std::sort(d.begin(), d.end(),
[](const std::pair<T, std::size_t>& l,
const std::pair<T, std::size_t>& r)
{
return l.first < r.first;
});
std::vector<std::size_t> indices(v.size());
std::transform(d.begin(), d.end(), indices.begin(),
[](const std::pair<T, std::size_t>& p) { return p.second; });
return indices;
}
Say in the resulting index vector the entry at position 0 is 8, then this tells you that the vector entries that have to go to the first position in the sorted vectors are those at position 8 in the original ranges.
You then use this information to sort all of your vectors:
template<typename T>
void sortByIndices(std::vector<T>& v,
const std::vector<std::size_t>& indices)
{
assert(v.size() == indices.size());
std::vector<T> result(v.size());
for(std::size_t i = 0; i < indices.size(); ++i)
result[i] = v[indices[i]];
v = std::move(result);
}
Any number of vectors may then be sorted like this:
const auto indices = computeSortIndices(v1);
sortByIndices(v1, indices);
sortByIndices(v2, indices);
// extend here for more vectors
This can be improved a bit by extracting the sorted v1 out of computeSortIndices directly, so that you do not need to sort it again using sortByIndices.
3) Implement your own sort function that is able to operate on multiple vectors. I have sketched an implementation of an in-place merge sort that is able to sort any number of vectors depending on the values in the first one.
The core of the merge sort algorithm is implemented by the multiMergeSortRec function, which takes an arbitrary number (> 0) of vectors of arbitrary types.
The function splits all vectors into first and second half, sorts both halves recursively and merges the the results back together. Search the web for a full explanation of merge sort if you need more details.
template<typename T, typename... Ts>
void multiMergeSortRec(
std::size_t b, std::size_t e,
std::vector<T>& v, std::vector<Ts>&... vs)
{
const std::size_t dist = e - b;
if(dist <= 1)
return;
std::size_t m = b + (dist / static_cast<std::size_t>(2));
// split in half and recursively sort both parts
multiMergeSortRec(b, m, v, vs...);
multiMergeSortRec(m, e, v, vs...);
// merge both sorted parts
while(b < m)
{
if(v[b] <= v[m])
++b;
else
{
++m;
rotateAll(b, m, v, vs...);
if(m == e)
break;
}
}
}
template<typename T, typename... Ts>
void multiMergeSort(std::vector<T>& v, std::vector<Ts>&... vs)
{
// TODO: check that all vectors have same length
if(v.size() < 2)
return ;
multiMergeSortRec<T, Ts...>(0, v.size(), v, vs...);
}
In order to operate in-place, parts of the vectors have to be rotated. This is done by the rotateAll function, which again works on an arbitrary number of vectors by recursively processing the variadic parameter pack.
void rotateAll(std::size_t, std::size_t)
{
}
template<typename T, typename... Ts>
void rotateAll(std::size_t b, std::size_t e,
std::vector<T>& v, std::vector<Ts>&... vs)
{
std::rotate(v.begin() + b, v.begin() + e - 1, v.begin() + e);
rotateAll(b, e, vs...);
}
Note, that the recursive calls of rotateAll are very likely to be inlined by every optimizing compiler, such that the function merely applies std::rotate to all vectors. You can circumvent the need to rotate parts of the vector, if you leave in-place and merge into an additional vector. I like to emphasize that this is neither an optimized nor a fully tested implementation of merge sort. It should serve as a sketch, since you really do not want to use bubble sort whenever you work on large vectors.
Let's quickly compare the above alternatives:
1) is easier to implement, since it relies on an existing (highly optimized and tested) std::sort implementation.
1) needs all data to be copied into the new vector and possibly (depending on your use case) all of it to be copied back.
In 1) multiple places have to be extended if you need to attach additional vectors to be sorted.
The implementation effort for 2) is mediocre (more than 1, but less and easier than 3), but it relies on optimized and tested std::sort.
2) cannot sort in-place (using the indices) and thus has to make a copy of every vector. Maybe there is an in-place alternative, but I cannot think of one right now (at least an easy one).
2) is easy to extend for additional vectors.
For 3) you need to implement sorting yourself, which makes it more difficult to get right.
3) does not need to copy all data. The implementation can be further optimized and can be tweaked for improved performance (out-of-place) or reduced memory consumption (in-place).
3) can work on additional vectors without any change. Just invoke multiMergeSort with one or more additional arguments.
All three work for heterogeneous sets of vectors, in contrast to the std::vector<std::vector<>> approach.
Which of the alternatives performs better in your case, is hard to say and should greatly depend on the number of vectors and their size, so if you really need optimal performance (and/or memory usage) you need to measure.
Find an implementation of the above here.
By far the easiest solution is to create a helper vector std::vector<size_t> initialized with std::iota(helper.begin(), helper.end(), size_t{});.
Next, sort this array,. obviously not by the array index (iota already did that), but by sortvector[i]. IOW, the predicate is [sortvector&](size_t i, size_t j) { sortVector[i] < sortVector[j]; }.
You now have the proper order of array indices. I.e. if helper[0]==17, then it means that the new front of all vectors should be the original 18th element. Usually the easiest way to produce the sorted result is to copy over elements, and then swap the original vector and the copy, repeated for all vectors. But if copying all elements is too expensive, it can be done in-place. (Note that if O(N) element copes are too expensive, a straightforward std::sort tends to perform badly as well as it needs pivots)

Sorting vector of arrays by array's element

I have encountered a problem when writing algorithm for solving Knapsack problem. I have a vector of 3-element arrays (C++11) and I want to sort the vector by the value of let's say first element of these arrays.
I've tried std::sort with predefined compare function, but it doesn't even compile.
I guess my compare function doesn't work as I expect:
bool compareByValue(const data &a, const data &b)
{
return a[0] < b[0];
}
int main()
{
vector<array<int, 3> > myVector;
...
sort ( myVector.begin(), myVector.end(), compareByValue );
}
It's not the first time when I have the similar problem, I tried to find solution on the Net, but without any satisfying result.
Also note that std::array has overloaded comparison operators, which compare arrays lexicographically. That means, if you want to sort based on the first element, you don't even need a predicate. Just std::sort your vector.
I don't know where you got data from, but you need to change it to array<int, 3> or make compareByValue a template:
bool compareByValue(const array<int, 3> &a, const array<int, 3>&b)
{
return a[0] < b[0];
}

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 find common words between two vectors of std::string

I am trying to find common words between 2 vectors of std::string. I want to get those into a sorted list which is sorted by length, and then words of each length to be sorted alphabetically. I need to use stl functions and functors.
My thoughts:
using a for_each go through first vector and for each word, compare it to the other vector using a functor (if common, append to a list in functor). Then the resulting list will have only common words in it. Here is where I am stuck, I know how to sort alphabetically, but how do I sort them by length and then sort the same length chunks alphabetically? I have looked around stl, but I am not finding what I need. Or, I am just thinking about this the wrong way. Any ideas?
Example:
vec1: "and", "thus", "it", "has", "a", "beginning", "and", "end"
vec2: "and," "therefore", "stars", "are", "beginning", "to","fall","to","their", "end"
result: "and", "end", "beginning"
If you are allowed to sort vec1 and vec2, you can use std::set_intersection to sort the vectors according to the criteria you specify and obtain the common elements, ordered by the same criteria:
#include <algorithm>
#include <iterator>
std::sort(vec1.begin(), vec1.end(), funny_comp);
std::sort(vec2.begin(), vec2.end(), funny_comp);
std::list<std::string> intersection;
std::set_intersection(vec1.begin(), vec1.end(),
vec2.begin(), vec2.end(),
std::back_inserter(intersection),
funny_comp);
where funny_comp compares by string length, and performs a lexicographical comparison of strings if these have the same length:
bool funny_comp(const std::string &lhs, const std::string &rhs)
{
return (lhs.size()) == rhs.size()) ? lhs < rhs
: lhs.size() < rhs.size();
}
See working demo here.
If the vectors are sorted you can use std::set_intersection() to find the words common to each. std::set_intersection() is O(N) time on the number of items. Sort of course, is O(N log N).
Your solution is O(n^2). This means if the length of the vectors is n, you're doing n*n operations: going over one vector, and for each element, going over the other vector to look for it.
If you can sort the vectors (using the sort function. No need for fancy sort like you mentioned), the time is O(n). using set_intersection. Even if you can't sort them - copy them into new vectors and sort those new vectors. It's sill much faster than what you're proposing.
To sort by length, then lexically, you need to define a comparison function (or functor) to do that:
struct by_len_lex {
bool operator()(std::string const &a, std::string const &b) {
if (a.length() < b.length())
return true;
if (a.length() > b.length())
return false;
return a < b;
}
};
// ...
std::sort(strings1.begin(), strings1.end(), by_len_lex());
std::sort(strings2.begin(), strings2.end(), by_len_lex());
// find intersection:
std::set_intersection(strings1.begin(), strings1.end(),
strings2.begin(), strings2.end(),
std::back_inserter(results),
by_len_lex());
Note that since you're defining the sort criteria, you need to specify the same criteria both when sorting and when doing the intersection.
This might not be the best solution, but can use map like following :
#include <iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int main()
{
vector <string> v1{"and", "thus", "it", "has",
"a", "beginning", "and", "end"};
vector <string> v2{"and" ,"therefore", "stars",
"are", "beginning", "to","fall","to",
"their", "end"};
map <string,int> m;
auto check=[&](const string& x) { return m.find(x) != m.end() ; } ;
for_each(v1.begin(),
v1.end(),
[&](const string& x){
m[x] =1;
}
);
for_each(v2.begin(),
v2.end(),
[&](const string& x){
if(check(x))
cout<<x<<endl;
}
);
}