How to modify the existing stl find function in C++? - c++

Given that I have a data structure,
struct data{
int val;
};
struct data A[LEN]; // LEN: some length.
// the below operator would be used in sorting.
bool operator < (struct data &a1, struct data &a2){
return a1.val < a2.val;
}
int main(){
// fill up A.
sort(A, A+LEN); // sort up A
/*Now I want something like this to happen ..
x = find(A, A+LEN, value); -> return the index such that A[index].val = value,
find is the stl find function ..
*/
}
How do you do that ?
And for any stl function how do you get to know which operators to override so that it works in the given condition ?

The modifications needed to find elements in such a case are pretty minimal. First, you want to make your operator< take its arguments as const references (technically not necessary for the current exercise, but something you want to do in general):
bool operator < (data const &a1, data const &a2){
return a1.val < a2.val;
}
Then (the part that really matters specifically for std::find) you also need to define an operator==:
bool operator==(data const &a, data const &b) {
return a.val == b.val;
}
Note, however, that you don't have to define this if you use a binary search instead:
auto pos = std::lower_bound(data, data+LEN, some_value);
This will just use the operator< that you'd already defined. If the items are already sorted anyway, this will usually be preferable (generally quite a bit faster unless LEN is quite small).

If you only want to make std::find work for your array of structure, you need to define operator== for struct data:
struct data
{
data(int value=0) : val(value) {}
int val;
};
bool operator==(const data& l, const data& r) { return l.val == r.val;}
auto x = find(A, A+LEN, value);
OR
auto x = find(A, A+LEN, data(value));
To get index of value in A, use std::distance
std::distance(A, x);
Note:
For more sufficent search with sorted container, use std::lower_bound, std::uppper_bound, std::binary_search instead.
auto lower = std::lower_bound(A, A+LEN, data(3));
auto upper = std::upper_bound(A, A+LEN, data(3));
Your operator< function signature better be like:
bool operator < (const data &a1, const data &a2)
// ^^^^^ ^^^^^

Related

Sorting a list by a struct member in c++

I have a list filled with this struct:
struct singlePaymentStruct
{
std::string payer;
int payment;
double amount;
std::time_t timeRec;
singlePaymentStruct() {
payer="Empty";
payment=0;
amount=0;
timeRec = time(0);
}
};
I want to be able to sort this list by any of the fields. How exactly do I do this?
I didn't quite understand how sort method works with something more complex than just a list of records...
Solution found:
singlePaymentList.sort( []( const singlePaymentStruct &a, const singlePaymentStruct &b)
{return a.payer > b.payer;}
);
1.overloading operator<
you can do this by overloading the < operator
struct Foo{
int bar;
bool operator<(Foo &x){
return bar < x.bar;
}
};
2.using lambda expressions
(what is lambda expression?)
Foo array[10];
std::sort(array,array + 10,[](Foo const &l, Foo const &r) {
return l.bar < r.bar; });
3.using custom compare functions
If the possible fields to be used for sorting are known prior, it may be easier to read to implement custom compare functions specifically for the sorting.
struct Foo {
int bar;
SpecialType daa; // Assume daa.IsLessThan() available.
static bool lessBar(const Foo& l, const Foo& r) {
return l.bar < r.bar;
}
static bool lessDaa(const Foo& l, const Foo& r) {
return l.daa.IsLessThan(r.daa);
}
};
Foo array1[10]; // To be sorted by Foo::bar
Foo array2[10]; // To be sorted by Foo::daa
std::sort(array1, array1+10, Foo::lessBar);
std::sort(array2, array2+10, Foo::lessDaa);
std::sort accepts a third optional parameter that is a comparator function. This function should behave as < between elements (i.e. return true when the first is "less than" the second.
For example to sort an std::vector of your structures on increasing payment value what you can do is:
std::sort(data.begin(), data.end(),
[](const singlePaymentStruct& a, const singlePaymentStruct& b) {
return a.payment < b.payment;
});
let the array be struct singlePaymentStruct a[N]
sort(a,a+N,cmp);
bool cmp(struct singlePaymentStruct x, struct singlePaymentStruct y)
{
return x.field < y.field ; //or anything you want to do and return boolean
}
How it works under the hood?
Simply put basically it uses some sorting algoritm like quicksort or mergesort.
Why do we specify comparator functor ?
Well we need that comparator functor to decide the ordering of elements.
The basic thing is in any sorting algortihm the basic operation is comparison..and if we can specify that we are basically controlling the sorting operation.
Hope now you get the pieces together. That's why cmp() takes two values which it will compare and based on which order them.

Operator for Multiple Points in One Struct

I have a struct that stores two points that should be interchangeable.
struct Edge
{
unsigned short firstIndex;
unsigned short secondIndex;
Edge(unsigned short firstIndex, unsigned short secondIndex) :
firstIndex(firstIndex), secondIndex(secondIndex) {}
};
The operator== method should be as follows (To make them interchangeable)
bool operator == (const Edge& e2) const
{
return
first == e2.first && second == e2.second ||
first == e2.second && second == e2.first;
}
I am looking to create an operator< and operator> method in order to use the struct in a std::map
I have tried the following (using multiplication) but it does not work since there are many cases in which different edges return the same value
bool operator < (const Edge& e2) const
{
return first * second < e2.first * e2.second;
}
The code that I would like to use is the following:
std::map<Edge, unsigned int> edgePoints;
Edge e1(0, 1);
Edge e2(1, 2);
Edge e3(2, 0);
edgePoints[e1] = 2;
edgePoints[e2] = 0;
edgePoints[e3] = 1;
Although the code does not work with my operator< method because 0 * 1 == 2 * 0 so the map returns 2 when I call edgePoints[e3]
Does anyone know of an operator< and operator> method that I could use or even some other way of mapping the edges in order to use the std::map
I would store the indices of the edge in such a way, that the smaller index always is the first index. It looks like the internal representation is irrelevant in your application. You don't need operator== for maps. Here is the example struct:
struct Edge
{
typedef unsigned short Idx; // prefer strong typedef cf boost
Edge(Idx a, Idx b)
:
firstIndex(std::min(a, b)),
secondIndex(std::max(a, b))
{}
Idx firstIndex;
Idx secondIndex;
bool operator<(Edge const & other)
{
if (firstIndex != other.firstIndex)
return firstIndex < other.firstIndex;
return secondIndex < other.secondIndex;
}
}; // Edge
If you want to make your implementation even nicer, some minor suggestions:
Prefer std::array<unsigned short, 2> over separate variables firstIndex and secondIndex. Doing so allows iterating over the indices.
If you are using array, you can shorten the operator< using std::lexicographical_compare.
Consider compairing them as sorted pairs.
bool operator < (const Edge& e2) const
{
if (min(first, second) != min(e2.first, e2.second))
return min(first, second) < min(e2.first, e2.second);
return max(first, second) < max(e2.first, e2.second);
}
Edit: Of course that can be written nicelier with saving mins and maxes as local variables, but the idea should be clear.
Edit: The idea in other answer is better: force your struct to always have first less then second, and it will eliminate all mins and maxes, and make comparation run fast like hell)

Binary search equivalent for `find_if`

Suppose a container (in this case a plain array) storing elements like
struct Foo
{
char id[8];
// other members
};
Now I want to find a Foo whose id begins with a particular string S. Since the array is sorted by id, I want to use binary search, so I look for a function which perform binary search with the same interface as find_if. Is there such a function in STL, can it be constructed by using other elements in algorithm, or do I need to implement it my self.
You are looking for std::lower_bound, std::upper_bound and std::equal_range, which take an input range, a search value and an optional comparator and require the range to be sorted according to comparator.
For your specific example, I'd use std::lexicographical_compare for the comparator:
#include <algorithm>
#include <iterator>
struct IdCmp
{
bool operator()(const Foo & lhs, const Foo & rhs) const
{
return std::lexicographical_compare(std::begin(lhs.id), std::end(lhs.id),
std::begin(rhs.id), std::end(rhs.id));
}
};
int main()
{
Foo a[100]; // populate
Foo b = make_needle();
auto p = std::equal_range(std::begin(a), std::end(a), b, IdCmp());
/* The elements with key equal to that of b are in [p.first, p.second). */
}
If you want to be able to search for strings directly, your comparator needs to be callable heterogeneously with one Foo argument and one string argument. For example:
struct IdCmp
{
bool operator()(const Foo & lhs, const Foo & rhs) const
{
return std::lexicographical_compare(std::begin(lhs.id), std::end(lhs.id),
std::begin(rhs.id), std::end(rhs.id));
}
bool operator()(const Foo & lhs, const char * id) const
{
return std::lexicographical_compare(std::begin(lhs.id), std::end(lhs.id),
id, id + 8);
}
bool operator()(const char * id, const Foo & rhs) const
{
return std::lexicographical_compare(id, id + 8,
std::begin(rhs.id), std::end(rhs.id));
}
};
Now you can search:
std::lower_bound(std::begin(a), std::end(a), "ABCD1234", IdCmp())
I believe you're looking for std::binary_search or std::lower_bound.

Alphabetically sort a list of objects by member in C++

I'm trying to create a function to sort a list of contacts in an address book by name or last name.
void sortList (list<Contact> & address_book){
//define two iterators - first to point to the first element from the list, second to the second element
list<Contact>::iterator it = address_book.begin();
list<Contact>::iterator it2 = address_book.begin();
it2++;
//get the last name for the first 2 contacts
string last_name1 = it->get_last_name();
string last_name2 = it2->get_last_name();
int i = 0;
while (i < last_name1.length() && i < last_name2.length()){
if (last_name1[i] < last_name2[i]){
swap(it, it2);
break;
}
}
}
I'm sure I'm not doing it correctly, but I'm a little bit lost with these iterators. I also know I should have another while loop to loop through all my contacts until all of them are sorted but, honestly I have no idea how to implement it.
std::list has an overloaded member function sort, that
Sorts the elements in ascending order. The order of equal elements is guaranteed to be preserved. The first version uses operator< to compare the elements, the second version uses the given comparison function comp.
To give the comparison function you can use functors:
struct sort_by_name {
bool operator()(const Contact &a, const Contact &b)
{ return a.get_name() < b.get_name(); }
};
struct sort_by_last_name {
bool operator()(const Contact &a, const Contact &b)
{ return a.get_last_name() < b.get_last_name(); }
};
or simpler free functions
bool cmp_by_name(const Contact &a, const Contact &b)
{ return a.get_name() < b.get_name(); }
bool cmp_by_last_name(const Contact &a, const Contact &b)
{ return a.get_last_name() < b.get_last_name(); }
and you call it either
address_book.sort(sort_by_name());
address_book.sort(sort_by_last_name());
or
address_book.sort(cmp_by_name);
address_book.sort(cmp_by_last_name);
the accessors get_name() and get_last_name() must be const.
Don't do your own sorting. Use std::sort(). You'll need to supply a custom comparator — something like this:
struct LastNameComp {
bool operator()(const Contact& a, const Contact& b) const {
return a.get_last_name() < b.get_last_name();
}
}
⋮
std::sort(address_book.begin(), address_book.end(), LastNameComp());
If you have access to a C++11 compiler, you can do better:
std::sort(address_book.begin(), address_book.end(),
[](const Contact& a, const Contact& b) {
return a.get_last_name() < b.get_last_name();
});
Expanding on the answers given using std::lexicographical_compare and lists internal sort.
struct LastNameComp {
bool operator()(const Contact& a, const Contact& b) {
return std::lexicographical_compare(
a.get_last_name().begin(), a.get_last_name().end(),
b.get_last_name().begin(), b.get_last_name().end(),
);
}
};
address_book.sort(LastNameComp());

sorting a vector of structs [duplicate]

This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 9 years ago.
I have a vector<data> info where data is defined as:
struct data{
string word;
int number;
};
I need to sort info by the length of the word strings. Is there a quick and simple way to do it?
Use a comparison function:
bool compareByLength(const data &a, const data &b)
{
return a.word.size() < b.word.size();
}
and then use std::sort in the header #include <algorithm>:
std::sort(info.begin(), info.end(), compareByLength);
Just make a comparison function/functor:
bool my_cmp(const data& a, const data& b)
{
// smallest comes first
return a.word.size() < b.word.size();
}
std::sort(info.begin(), info.end(), my_cmp);
Or provide an bool operator<(const data& a) const in your data class:
struct data {
string word;
int number;
bool operator<(const data& a) const
{
return word.size() < a.word.size();
}
};
or non-member as Fred said:
struct data {
string word;
int number;
};
bool operator<(const data& a, const data& b)
{
return a.word.size() < b.word.size();
}
and just call std::sort():
std::sort(info.begin(), info.end());
Yes: you can sort using a custom comparison function:
std::sort(info.begin(), info.end(), my_custom_comparison);
my_custom_comparison needs to be a function or a class with an operator() overload (a functor) that takes two data objects and returns a bool indicating whether the first is ordered prior to the second (i.e., first < second). Alternatively, you can overload operator< for your class type data; operator< is the default ordering used by std::sort.
Either way, the comparison function must yield a strict weak ordering of the elements.
As others have mentioned, you could use a comparison function, but you can also overload the < operator and the default less<T> functor will work as well:
struct data {
string word;
int number;
bool operator < (const data& rhs) const {
return word.size() < rhs.word.size();
}
};
Then it's just:
std::sort(info.begin(), info.end());
Edit
As James McNellis pointed out, sort does not actually use the less<T> functor by default. However, the rest of the statement that the less<T> functor will work as well is still correct, which means that if you wanted to put struct datas into a std::map or std::set this would still work, but the other answers which provide a comparison function would need additional code to work with either.