Sort a vector using its elements - c++

i need to know how can we sort a vector of user define class using its elements.
Say i have a class called "coordinates" with getX and getY method that return an int value.
I have created and array of vector "vector PointTwoD vcP2D(5);"
class coordinates {
int getX();
int getY();
)
Now the issue,
1) I need to sort the vector "vcP2D" using getX() and sort in asc order
2) Say an user enters the "2" as the x coordinate. And using that info i need to find which vector contains 2
Please advice

This will do:
std::sort(v.begin(), v.end(), [](const coordinates& c, const coordinates& d){ return c.getX() < d.getX(); });
It uses a C++11 Lambda expression as a binary predicate for std::sort.
A short demonstration:
#include <algorithm>
#include <vector>
#include <iostream>
struct coordinates
{
int x;
int y;
};
int main()
{
std::vector<coordinates> v{ {2,3}, {0,0}, {1,5} };
std::sort(v.begin(), v.end(), [](const coordinates& c, const coordinates& d) { return c.x < d.x; });
std::cout << "sorted by x values, values of \"x\": " << v[0].x << " " << v[1].x << " " << v[2].x << "\n";
std::sort(v.begin(), v.end(), [](const coordinates& c, const coordinates& d) { return c.y < d.y; });
std::cout << "sorted by y values, values of \"x\": " << v[0].x << " " << v[1].x << " " << v[2].x << "\n";
}
A demo of how to find an element in the same way:
#include <algorithm>
#include <vector>
#include <iostream>
struct coordinates
{
int x;
int y;
};
int main()
{
std::vector<coordinates> v{ {2,3}, {0,0}, {1,5} };
auto result = std::find_if(v.begin(), v.end(), [](const coordinates& c){ return c.x == 1 && c.y == 5; });
if(result != v.end())
std::cout << "point (1,5) is number " << std::distance(v.begin(), result)+1 << " in the vector.\n";
else
std::cout << "point (1,5) not found.\n";
}
If you are looking to search in the sorted vector, you can use std::binary_search which takes a comparison function (the same as std::sort above). It also does not give an iterator to that element, only a true or false.

You need to define a strict weak order on your elements, either using operator< () or a binary predicate, and then use std::sort().
The easiest approach is to create a less than operator<():
bool operator< (coordinates const& c0, coordinates const& c1) {
// return a suitable result of comparing c0 and c1 such that operator<()
// become a strict weak order
}
With this all you need to do to sort a std::vector<coordinates> is to use std::sort(). To locate a specific object you would use std::lower_bound().

Related

An unordered set of structs

I have been trying to make an unordered_set of structs in c++ but it seems to give me this error-
error: call to implicitly-deleted default constructor
of 'std::__1::hash<coor>'
__compressed_pair_elem(__default_init_tag) {}
I included a == operator, can assist me in making a unordered_set of structures?
#include <iostream>
#include <vector>
#include <unordered_set>
#include <algorithm>
using namespace std;
struct coor{
int x,y;
bool operator==(coor a) const{
if(a.x == x && a.y == y){
return true;
}
else return false;
};
};
int main(){
unordered_set<coor> myset;
}
std::unordered_set uses hashes to identify objects uniquely. It computes the hash using std::hash<> structure. This is fine for primitive or STL containers as they have defined a hash structure for them. But for your case, there isn't a hash structure to generate the hash. The error says it: std::__1::hash<coor>.
To resolve this, we need to implement a std::hash<> for coor.
namespace std {
template<>
struct hash<coor> {
const size_t operator()(const coor& c) const
{
return std::hash<int>()(c.x) ^ std::hash<int>()(c.y);
}
};
}
Now the std::unordered_set has the required hash structure to compute the hash.
Many people use the solution given in the cppreference. Please see here.
But they always open the std namespace and specialize the template for std::hash
Basically that is not necessary.
According to the definition of the std::unordered_set as defined here, we need a hash function and an equal function. And for the hash function we just need an operator().
So, we can make this easily as a part of our struct. We need to overide the ()-operator and implement the hash function. And then we need to override the ==-operator to get the equal function.
Then we add the our struct name as an additional template parameter.
In my humble opinion, it is better to encapsulate the hash and equal functions in our struct, because only this datatype should know, how to calculate values.
Please see here:
#include <unordered_set>
#include <vector>
#include <iostream>
struct coor {
int x{}, y{};
// Hash function
size_t operator()(const coor& c) const { return std::hash<int>()(c.x) ^ std::hash<int>()(c.y); }
// Equal Function
bool operator==(const coor& other) const { return (x == other.x && y == other.y); }
};
int main() {
// Some test values
std::vector<coor> test{ {1,2}, {3,4}, {5,6}, {1,2} };
// Define the unordered set anf fill it with test value
std::unordered_set<coor, coor> myset(test.begin(), test.end());
// Show result. There is no double value 1,2
for (const coor& c : myset) std::cout << c.x << ' ' << c.y << '\n';
return 0;
}
Of course you may also Lambdas as "free" functions for the hash and the equal.
In the following example, we will define 2 Lambdas, one for calculating the hash and one calculating the equal function.
Since the type of the Lambda is only known to the compiler, we use decltype in the template parameter, to inform the compiler, which function "type" we will use in the template.
As an example I defined variable definitions using all 5 available constructors. Please see here for the description.
Please note: You can always and everywhere delete the "myEqual" function, if you add the operator== to the struct as shown above.
Please see the next example below:
#include <unordered_set>
#include <vector>
#include <iostream>
struct coor {
int x{}, y{};
};
constexpr size_t BucketCount = 20u;
int main() {
// Hash function
auto myHash = [](const coor& c) { return std::hash<int>()(c.x) ^ std::hash<int>()(c.y); };
// Equal function
auto myEqual = [](const coor& c1, const coor& c2) { return c1.x == c2.x && c1.y == c2.y; };
// Constructor Number 1
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> myset1(BucketCount, myHash, myEqual);
myset1.insert({ 1,2 }); myset1.insert({ 3,4 }); myset1.insert({ 5,6 }); myset1.insert({ 1,2 });
// Constructor Number 2
std::vector<coor> test{ {1,2}, {3,4}, {5,6}, {1,2} };// Some test values
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> myset2(test.begin(),test.end(), BucketCount, myHash, myEqual);
// Constructor Number 3
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> myset3(myset2);
// Constructor Number 4
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> mysetTemp(myset2);
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> myset4(std::move(mysetTemp));
// Constructor Number 5
std::unordered_set<coor, decltype(myHash), decltype(myEqual)> myset5({ {1,2}, {3,4}, {5,6}, {1,2} }, BucketCount, myHash, myEqual);
// Show result. There is no double value 1,2
for (const coor& c : myset1) std::cout << c.x << ' ' << c.y << '\n'; std::cout << '\n';
for (const coor& c : myset2) std::cout << c.x << ' ' << c.y << '\n'; std::cout << '\n';
for (const coor& c : myset3) std::cout << c.x << ' ' << c.y << '\n'; std::cout << '\n';
for (const coor& c : myset4) std::cout << c.x << ' ' << c.y << '\n'; std::cout << '\n';
for (const coor& c : myset5) std::cout << c.x << ' ' << c.y << '\n'; std::cout << '\n';
return 0;
}
Developed compiled and tested with Microsoft Visual Studio Community 2019, Version 16.8.2
Additionally compiled and tested with gcc10 amnd clang 11
Language: C++17
If you have any questions to the above, then I am happy to answer

I want to reverse the values of map and print it using range based for loop.

I have done the programming but it is not reversing. I have used a different map to put the values in reverse order,but it still shows the same. My main question was to traverse backward and print the values using range based loop.
#include "stdafx.h"
#include <iostream>
#include<conio.h>
#include <stdio.h>
#include<vector>
#include<map>
#include<utility>
#include<set>
map<int, int>m1;
for (int i = 1; i <= 100; ++i)
{
m1.insert({ i,i });
}
for (const auto &y :m1)
{
cout <<"("<< y.first << " "<<y.second << ")" <<" " ;
}
cout << endl << endl;
map<int, int>m2;
map<int, int>::reverse_iterator iter;
for (auto iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
m2.insert({ iter->first,iter->second });
}
for (const auto &y : m2)
{
cout << "(" << y.first << " " << y.second << ")" << " ";
}
As Some Programmer Dude pointed out, but for the completeness of my answer, a std::map is sorted on the key, no matter what order you insert the elements. One option would be to create a new map with the opposite sorting, but that doesn't seem to be what you really want.
It seems you know how about reverse iterators, but not how to get at them when using range-based for. Since it operates on a range, i.e. some type that provides begin and end iterators, you need to create some wrapper around your map that provides this.
Here's a general one I just put together than works in C++11. It won't cover every possible case, and can be made a bit neater in C++14, but it will work for you.
#include <iostream>
#include <iterator>
// The wrapper type that does reversal
template <typename Range>
class Reverser {
Range& r_;
public:
using iterator_type = std::reverse_iterator<decltype(std::begin(r_))>;
Reverser(Range& r) : r_(r) {}
iterator_type begin() { return iterator_type(std::end(r_)); }
iterator_type end() { return iterator_type(std::begin(r_)); }
};
// Helper creation function
template <typename Range>
Reverser<Range> reverse(Range& r)
{
return Reverser<Range>(r);
}
int main()
{
int vals[] = {1, 2, 3, 4, 5};
for (auto i : reverse(vals))
std::cout << i << '\n';
}
This outputs:
$ ./reverse
5
4
3
2
1
(You may also find libraries that provide a similar adapter; Eric Niebler is working on a ranges library for The Standard.)
Also, please reconsider your use of what are often considered bad practices: using namespace std; and endl (those are links to explanations).
Here's an example of iterating backward through a std::map:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, int> m;
m[1] = 1;
m[2] = 2;
m[3] = 3;
for (auto iter = m.rbegin(); iter != m.rend(); ++iter) {
std::cout << iter->first << ": " << iter->second << std::endl;
}
}
If you are pre-C++11, you'll just need to spell out auto, which is:
std::map<int, int>::reverse_iterator
If you're using boost, you can use a range-based for loop with a reverse adapter:
#include <boost/range/adaptor/reversed.hpp>
for (auto& iter : boost::adaptors::reverse(m)) {
std::cout << iter.first << ": " << iter.second << std::endl;
}
If you only need to print the elements in the map in reverse order,you don't need another map for it,you can do this:
std::map<int, int>::reverse_iterator iter;
for (iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
std::cout << "(" << iter->first << " " << iter->second << ")" << " ";
}

How sort double vector according to changes in first vector?

I would like to implement something like DoubleVector.
In this class I would also like to implement sort method, which sort v1_ and according to changes in v1_ the order in v2_ will also change.
The code is below:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class DoubleVector
{
vector<int> v1_;
vector<char> v2_;
public:
void sort()
{
//sort v1_ and also change order in v2_ according to changes in v1_
std::sort(v1_.begin(), v1_.end() /*, lambda ?*/);
}
void add(int value_v1, char value_v2)
{
v1_.push_back(value_v1);
v2_.push_back(value_v2);
}
void print()
{
const auto size = v1_.size();
for (size_t i=0;i<size;++i)
{
cout << v1_[i] << " " << v2_[i] << endl;
}
}
};
int main()
{
DoubleVector dv;
dv.add(6, 'g');
dv.add(2, 'r');
dv.add(3, 'y');
dv.add(4, 'a');
cout << "Before sort:" << endl;
dv.print();
dv.sort();
cout << "After sort:" << endl;
dv.print();//the values in v2_ are in the same order they don't change order according to v1_ changes
return 0;
}
As you can see DoubleVector before sort contains:
6 g
2 r
3 y
4 a
And after sort contains:
2 g
3 r
4 y
6 a
I would like to get:
2 r
3 y
4 a
6 g
So the first vector v1_ has been sorted, but the second still has got the same order and I would like to change order of elements in second v2_ vector according to changes in v1_.
I can write it, but I would like to do it in a fast and clean way, maybe using lambda as third argument in std::sort function? Vectors v1_ and v2_ in class DoubleVector must stay as they are.
Thank you very much.
Make a vector of std::pair<int,char> instead. Since operator < on the pair compares first and decides ties on the second, sorting std::vector<std::pair<int,char>> will produce the exact effect that you want:
vector<pair<int,char>> v;
v.push_back(make_pair(6, 'g'));
v.push_back(make_pair(2, 'r'));
v.push_back(make_pair(3, 'y'));
v.push_back(make_pair(4, 'a'));
sort(v.begin(), v.end());
for (int i = 0 ; i != v.size() ; i++) {
cout << v[i].first << " " << v[i].second << endl;
}
Demo.
You can do something like this:-
vector< pair<int,char> >v;
//do what you want
sort(v.begin(),v.end())
The sort function by default sorts according to first value but you can always define according to which criteria should the sort work
C++ STL - How does the third argument of the STL sort() work?
Try the following.
The way it works is to sort the position key pair based on the int vector value only and then use this ordering to extract values.
#include <iostream>
#include <algorithm>
#include <vector>
class dv
{
std::vector<int> _v1;
std::vector<char> _v2;
std::vector<std::pair<int, int> > _order;
public:
inline bool operator() (const std::pair<int, int>& v1_index_1,
const std::pair<int, int>& v1_index_2) const {
return _v1[v1_index_1.first] < _v1[v1_index_2.first];
}
void sort() {
std::sort(_order.begin(), _order.end(), *this);
}
void add(int value_v1, char value_v2) {
_order.push_back(std::pair<int, int>(_v1.size(), _v2.size()));
_v1.push_back(value_v1);
_v2.push_back(value_v2);
}
void print() {
const auto size(_v1.size());
for (size_t i=0; i<size; ++i) {
std::cout << _v1[_order[i].first]
<< " "
<< _v2[_order[i].second]
<< std::endl;
}
}
};
int main() {
dv dv;
dv.add(6, 'g');
dv.add(2, 'r');
dv.add(3, 'y');
dv.add(4, 'a');
std::cout << "before sort: " << std::endl;
dv.print();
std::cout << "sorting: " << std::endl;
dv.sort();
std::cout << "after sort: " << std::endl;
dv.print();
return 0;
}

Rewriting ( set<pair<x,y>> ) as a struct

I have to write this:
set<pair<float,int>>foo;
foo.insert(make_pair(a, b));
while (!foo.empty())
{
float dist = foo.begin()->first;
int u = foo.begin()->second;
}
But I want to use my own implementation without adding all the functionality of set/pair.
This is basically what I want pair to do:
struct pair2
{
float first;
int second;
pair2(float arg_first, int arg_second)
{
first = arg_first;
second = arg_second;
}
pair2 make_pair2(float first2, int second2)
{
return (pair2(first2,second2));
}
};
I'm not sure if that's completely correct, but I also have to implement set to work with it:
set<pair>foo; //not set<pair<float,int>>foo; since not templated
foo.insert(make_pair2(a,b));
What's the best way to do this? Should I implement set using a vector? All I need set to have is: empty(), begin(), erase() and insert() which vectors already have.
If I understand correctly, you want to have a pair that is "untemplated". You can type alias, which you can use to rename types to some custom type.
Instead of typing std::pair<float, int> each time, you could call it my_pair.
This is how you can do it in C++11:
#include <iostream>
#include <utility>
#include <set>
using my_pair = std::pair<float, int>;
my_pair make_my_pair(float a, int b) {
return std::make_pair(a, b);
}
int main() {
my_pair a(1.5, 2);
my_pair b = make_my_pair(2.2, 3);
std::cout << a.first << " " << a.second << std::endl;
std::cout << b.first << " " << b.second << std::endl;
std::set<my_pair> foo;
foo.insert(make_my_pair(3.4, 4));
return 0;
}
Be sure to compile with -std=c++11.
If you can't/don't want to use C++11 constructs, you can do this as well:
#include <iostream>
#include <utility>
#include <set>
typedef std::pair<float, int> my_pair;
my_pair make_my_pair(float a, int b) {
return std::make_pair(a, b);
}
int main() {
my_pair a(1.5, 2);
my_pair b = make_my_pair(2.2, 3);
std::cout << a.first << " " << a.second << std::endl;
std::cout << b.first << " " << b.second << std::endl;
std::set<my_pair> foo;
foo.insert(make_my_pair(3.4, 4));
return 0;
}

Sort std::vector<myclass> in one line using sort function from STL

Question is about sorting std::vector<myclass> using function sort from STL's algorithms class.
Standard way is : sort(v.begin(), v.end(), &myfunct)
where myfunct is:
bool myfunct( myclass first, myclass second ) {
if (first.value < second.value)
return true;
else return false;
}
Approach above takes more than one line. I am curious how to do it in one line. Is it possible define function that compares myclass objects inside sort function? May be somehow use this (a < b) ? a : b. I remember that there is something like this in C#, but I forgot how is it called. Is it possible to do in C++.
First, you can just return first.value < second.value but this doesn't get rid of the function. In C++2011 you can use a lambda function:
std::sort(begin, end, [](myclass const& f, myclass const& s){ return f.value < s.value; });
Without C++2011 I think you'll need a function object because there isn't anything which projects your class to the value you actually want to compare.
BTW, you definitely want to pass everything but the most trivial objects by reference to your comparison function.
You could use boost::lambda and boost::lambda::bind (with boost lambda placeholders)
std::sort(vec.begin(), vec.end(),
boost::lambda::bind(&A::a, boost::lambda::_1)
<
boost::lambda::bind(&A::a, boost::lambda::_2));
sort passes 2 values to the comparison function so you need to compare those 2 values. The bind part of code just selects variable a from the struct A from each structures being compared (referenced by _1 and _2).
Example code:
#include <iostream>
#include <algorithm>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/array.hpp>
struct A
{
A() : a(0), b(0) {}
int a;
int b;
};
std::ostream & operator<<(std::ostream & os, A & a)
{ return os << a.a << ":" << a.b; }
int main()
{
boost::array<A,5> vec;
std::fill(vec.begin(),vec.end(),A());
vec[0].a = 1;
vec[1].a = 3;
vec[2].a = 4;
vec[3].a = 0;
vec[4].a = 2;
std::for_each(vec.begin(),vec.end(), std::cout << boost::lambda::_1 << ' ');
std::cout << std::endl;
std::sort(vec.begin(), vec.end(),
boost::lambda::bind(&A::a, boost::lambda::_1)
<
boost::lambda::bind(&A::a, boost::lambda::_2));
std::for_each(vec.begin(),vec.end(), std::cout << boost::lambda::_1 << ' ');
std::cout << std::endl;
}
Output:
1:0 3:0 4:0 0:0 2:0
0:0 1:0 2:0 3:0 4:0
why not copy the vector into a set:
std::copy(v.begin(),v.end(),std::inserter(s,s.end()));
Now the elements in the set are sorted in ascending order and use set now.
A one liner call to sort() : sort(my_vector_of_class_object.begin(),my_vector_of_class_object.end(),compare);
Code of a working demo of a "sort vector of class objects" is provided below:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class my_Class
{
public:
my_Class(int r,int n, int s):rollno(r),name(n),status(s) { }
int getRollno() const { return rollno;}
int getName() const { return name;}
int getStatus() const { return status;}
private:
int rollno;
int name;
int status;
};
bool compare(const my_Class& x, const my_Class& y) {
return x.getRollno() < y.getRollno();
}
int main()
{
vector<my_Class> my_vector_of_class_object;
vector<my_Class>::const_iterator iter;
my_Class s1(10,20,30);
my_Class s2(40,50,60);
my_Class s3(25,85,9);
my_Class s4(1,50,2);
my_Class s5(90,70,90);
my_Class s6(85,85,3);
my_Class s7(20,6,89);
my_Class s8(70,54,22);
my_Class s9(65,22,77);
my_vector_of_class_object.push_back(s1);
my_vector_of_class_object.push_back(s2);
my_vector_of_class_object.push_back(s3);
my_vector_of_class_object.push_back(s4);
my_vector_of_class_object.push_back(s5);
my_vector_of_class_object.push_back(s6);
my_vector_of_class_object.push_back(s7);
my_vector_of_class_object.push_back(s8);
my_vector_of_class_object.push_back(s9);
cout <<"Before vector sort \n";
for(iter=my_vector_of_class_object.begin(); iter!=my_vector_of_class_object.end();++iter)
std::cout << (*iter).getRollno() << '\t' << (*iter).getName() << '\t' << (*iter).getStatus() << '\n';
cout <<" \n\n";
sort(my_vector_of_class_object.begin(),my_vector_of_class_object.end(),compare);
cout <<"After vector sort \n";
for(iter=my_vector_of_class_object.begin(); iter!=my_vector_of_class_object.end();++iter)
std::cout << (*iter).getRollno() << '\t' << (*iter).getName() << '\t' << (*iter).getStatus() << '\n';
cout <<" \n\n";
return 0;
}