std::set. Custom set of sets - c++

Word.
I have a struct, containing a single field that I would like set to use for comparison and equivalence, and other fields as metadata:
struct read_tag{
unsigned int read_id; // want std::set to use this
int offset; // metadata
bool orientation; // metadata
};
I have a functor to do the job:
struct read_tag_compare {
bool operator() (const read_tag &a, const read_tag &b) const {
return a.read_id > b.read_id
}
};
and decl. the required set as
std::set<read_tag, read_tag_compare> block;
Everything so far compiles. The problem is below:
How do I make a set containing std::set<read_tag, read_tag_compare>. I want something like this:
std::set< std::set<read_tag, read_tag_compare> > blocks;
blocks.insert(a_block); // comp error
But this gives me a very large, and hard to decipher error.
I thought it would recursively check how the inner sets are compared and extend this to the outer sets. All one had to do is define the comparator for the inner most set.
For example
std::set<std:set<unsigned int>> set_o_sets;
works fine, without me having to define how to compare std::set<unsigned int>
Any help is mucho appreciated :D

The <-comparison on std::set uses std::lexicographical_compare without comparator, i.e. it just forwards to < on the element type. (This is a limitation of the standard library, since this is defined for all containers, not just the ordered-associative ones.) So what you need is a custom comparator for sets of sets that uses the correct overload of lexicographical comparison:
using read_tag_set = std::set<read_tag, read_tag_compare>;
struct read_tag_set_compare {
bool operator()(const read_tag_set &a, const read_tag_set &b) const noexcept {
return std::lexicographical_compare(a.begin(), a.end(),
b.begin(), b.end(), a.key_comp());
// ^^^^^^^^^^^^
}
};
Now use: std::set<read_tag_set, read_tag_set_compare>
The code shows why there isn't an obvious "fix" to the ordered associative containers that would make this "just work": If the containers use custom, stateful predicates, then it's not in general guaranteed that the members of two distinct containers can actually be compared with one another at all. All you know is that the elements within one container are comparable with that container's comparator. So when you're using a custom comparator, you better also say explicitly how two distinct containers relate, and you assert explicitly that it makes sense to compare two containers.

It compiled with no error with my g++-5.3.1 ubuntu..
#include<set>
#include<iostream>
using namespace std;
struct read_tag{
unsigned int read_id; // want std::set to use this
int offset; // metadata
bool orientation; // metadata
};
struct read_tag_compare {
bool operator() (const read_tag &a, const read_tag &b) const {
return a.read_id > b.read_id;
}
};
struct read_compare {
bool operator() (const set<read_tag, read_tag_compare> &a, const set<read_tag, read_tag_compare> &b) const {
return true;
}
};
int main()
{
set<read_tag, read_tag_compare> block;
set<set<read_tag, read_tag_compare>, read_compare> blocks;
blocks.insert(block)
}
Above was what I compiled.

Related

How to make a map sort by value C++

I was trying to make a map sort by value using a custom comparator but I couldn't figure out why I kept getting the error of "no matching call to compareByVal"
Here's what I had in my main.cpp:
#include <map>
#include <iostream>
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
int main() {
std::map<int,int,compareByVal> hash;
hash[1] = 5;
hash[2] = 2;
hash[3] = 10;
std::cout << hash.begin()->first << std::endl;
}
The first, simple problem is
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
should be
struct compareByVal {
bool operator()(const std::pair<int,int> & a, const std::pair<int,int> & b) const {
return a.second < b.second;
}
};
The second, serious problem is the signature of the compare is wrong. It should be
struct compareByVal {
bool operator()(const int leftKey, const int rightKey) const;
}
You can't access the value in the compare function. There is no (simple) way to sort a map by value.
Simply put, you cannot. Not sure which compiler you're using, but clang and gcc both give useful messages. with context.
clang:
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
gcc:
if (__i == end() || key_comp()(__k, (*__i).first))
You can see that clang and gcc are both calling the compare method with only they key, and not a value. This is simply how maps work.
If you want to sort by value, you would have to create your own custom map, or, more realistically, use the value as the key instead. Creating your own map to achieve this would be more difficult than you'd think, since it would have to sort after any value is modified.
If you want to sort a std::map by its value, then you are using the wrong container. std::map is sorted by the keys by definition.
You can wrap key and value:
struct foo {
int key;
int value;
};
and then use a std::set<foo> that uses a comparator that only compares foo::value.
Well, first, the reason you're getting the error: "no matching call to compareByVal" is because map's comparator works only with the keys. So the comparator should like:
struct compareByVal {
template <typename T>
bool operator()(const T& a, const T& b) const
return a < b;
}
Coming on to what you want to achieve, I see two ways of doing so:
Copy all the elements of the map to a std::vector and sort that:
std::vector<std::pair<int,int> > v(hash.begin(), hash.end());
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
Copy all the elements of the map to another map with keys as values and values as keys. If values of your map are not unique, you can use a std::multimap instead.
This may be an X-Y issue.
If you need to sort by both key and value, then a single std::map may not be the most efficient choice.
In database theory, all the data would be placed into a single table. An index table would be created describing the access or sorting method. Data that needs to be sorted in more than one method would have multiple index tables.
In C++, the core table would be a std::vector. The indices would be std::map<key1, vector_index>, std::map<key2, vector_index>, where vector_index is the index of the item in the core table.
Example:
struct Record
{
int age;
std::string name;
};
// Core table
std::vector<Record> database;
// Index by age
std::map<int, unsigned int> age_index_table;
// Index by name
std::map<std::string, unsigned int> name_index_table;
// Fetching by age:
unsigned int database_index = age_index_table[42];
Record r = database[database_index];
// Fetching by name:
unsigned int database_index = name_index_table["Harry Potter"];
Record r = database[database_index];
You can learn more by searching the internet for "database index tables c++".
If it looks like a database and smells like a database ...

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.

How to create a set with my customized comparison in c++

Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.

std::map - how to change key sorting?

I have a problem with std::map.
I'm using it to map some list of pairs under a specific index:
map<string, list<pair<string, int> > > List;
It's used in Dijkstra algorithm.
The main problem is that map sorts string keys in alphabetical order, like this:
AAA, AA0, AA1, AAB, AC1 = AA0->AA1->AAA->AAB->AC1
But I would like to sort it in a different way:
AAA, AA0, AA1, AAB, AC1 = AAA->AAB->AA0->AA1->AC1
Is there any solution to this? I read about making my own comparing class, but I have no idea how to do this. Or maybe there's some other way to solve it?
You have to provide your own comparison functor, which must be passed as 3rd template parameter when instantiating the map. For example:
struct Comp
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
// implement your comparison logic here
}
};
Instances of this class is callable (hence "functor") with two string parameters, and should return true or false based in a strict weak ordering logic.
Then instantiate the map using the functor type:
std::map<string, list<pair<string, int>>, Comp> List;
Now the map will use your comparison logic internally to define the ordering of its elements.
Like others have said, you need to implement a custom comparer...
struct custom_comparer
{
bool operator()(const std::string& left, const std::string& right) const
{
return std::lexicographical_compare(
left.cbegin(), left.cend(),
right.cbegin(), right.cend(),
[](char l, char r) -> bool
{
bool ldigit = isdigit(l) != 0,
rdigit = isdigit(r) != 0;
return (!ldigit && rdigit) || (ldigit == rdigit && l < r);
});
}
};
And use it...
std::map<string, list<pair<string, int>>, custom_comparer> List;
Normal string comparison operators use lexicographical_compare. My custom_comparer above also uses it, but with a custom comparer plugged in. The custom comparer uses isdigit to do the comparison you want.
You have to write your own comparer:
struct custom_string_comparer
{
bool operator()(const std::string& s1, const std::string& s2)
{
return ...; // your comparison here
}
};
map<string, list<pair<string, int>>, custom_string_comparer> List;
Yes. You need to supply a third template argument, see the docs.

C++ STL--make_heap with pair<int,string> as data type

I know how does heap work and how it arranges min and max elements. It is easy, if vector contains only int, to apply make_heap in STL. But how to apply make_heap() if vector contains structure of string and int. Iwant to make heap based on int value in structure.
Please tell me how to do that.
You have to provide comparison function for your structure:
struct A
{
int x, y;
};
struct Comp
{
bool operator()(const A& s1, const A& s2)
{
return s1.x < s2.x && s1.y == s2.y;
}
};
std::vector<A> vec;
std::make_heap(vec.begin(), vec.end(), Comp());
Yes, you can use std::make_heap with std::pair<int, std::string> directly because std::pair has the required less-than comparison operator<. There is even an example in the reference linked above using that particular instantiation of std::pair.