This question already has answers here:
How can I use std::maps with user-defined types as key?
(8 answers)
C++ unordered_map using a custom class type as the key
(7 answers)
Closed 3 years ago.
Problem description
I defined a std::map as:
struct Key // Key of my dicitonary
{
float index[3];
};
struct Value // Value of my dictionary
{
float color[3];
float operator[](int &a) const { return color[a]; }
};
Key key = {a, b, c};
Value value = {d, e, f};
std::map<Key, Value> dict = new std::map<Key,Value>;
if (dict.count(key) < 1) { // If key does not exist, add value
addrDict[key] = value;
}
Problem
When I run the code, I get the following error:
d:\documents\mingw\lib\gcc\mingw32\8.2.0\include\c++\bits\stl_function.h:386:20: error: no match for 'operator<' (operand types are 'const Key' and 'const Key')
As far as I understand, it cannot compare two Key object in the map, which is ordered.
For this reason, I need to implement the operator< in the Key struct (even if I don't care about the order of the keys, but using an unordered map returns compiler errors probably related to g++). I just need to keep the keys composed of three different arrays separated. Any idea on how to achieve that?
What I have done
I implemented the operator with a trial and error approach and this solution kind of return what I am expecting, but it does not store all the possible keys:
bool operator<(const Key &rhs) const {
(index[0]*index[1]*index[2]) < (rhs.index[0]* rhs.index[1]* rhs.index[2]);
};
If, on the other hand, I do this:
bool operator<(const Key &rhs) const {
index[0] < rhs.index[0];
};
I only store a few keys, and not all the possible combinations of keys.
EDIT:
solved a problem of variable definition
Related
This question already has answers here:
operator< comparing multiple fields
(6 answers)
Closed 3 years ago.
Consider implementing operator< for the following class:
struct foo {
int a, b;
};
Probably the most common way is something like the element-wise lexicographic compare, like so:
bool operator<(foo lhs, foo rhs) {
return lhs.a < rhs.a || (lhs.a == rhs.a && lhs.b < rhs.b);
}
I've written that enough times, and made a type enough times that I'm wondering if there is something built in to std:: that will do it for me, hopefully with less boilerplate and reasonable code generation.
Now the above for two elements isn't that bad, but the terms multiply as you add more members.
You can use pair<int, int> instead of struct foo.
As pair<type,type> is builtin in std:: it has comparator for all builtin data type. So you don't need any extra operator overloading or a custom comparator. Even it works for strings. std:: compare two pair<type, type> based on the first element of the pair. If it's a tie then the tie is broken based on the second element.
For example-
pair<int,int> a = make_pair(5,10) //declare using make_pair()
pair<int,int> b = {5,7} //different way of declaring pair.
pair<int,int> c = pair<int,int>(7,7) //declare using constructor
Now a < b == false, b < c == true and a < c == true
So in this way, you can use pair<type, type> instead of struct foo. You can replace 'type' using any data type like float, long long, char, string, pair, vector, set, map, complex etc.
This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 3 years ago.
In the below C++ snippet,
HOW TO SORT the vector "TwoIntsVec" BASED ON the element "int a" in TwoInts struct. i.e. i need to place the "TwoIntsVec[i] which has the least "TwoIntsVec[i].a" in the 1st place and so on in increasing order of "TwoIntsVec[i].a".
In the below example the vector elemnt struct having 7,3 should be placed 1st as 7 is the least "a" and so on.
struct TwoInts
{
int a;
int b;
};
void PushToVector(int a, int b, std::vector<TwoInts>& TwoIntsVec)
{
TwoInts temp;
temp.a = a;
temp.b = b;
TwoIntsVec.push_back(temp);
}
int main()
{
std::vector<TwoInts> TwoIntsVec;
PushToVector(21,3,TwoIntsVec);
PushToVector(7,3,TwoIntsVec);
PushToVector(12,3,TwoIntsVec);
PushToVector(9,3,TwoIntsVec);
PushToVector(16,3,TwoIntsVec);
// Below sort would NOT work here, as TwoIntsVec is
// not a std::vector<int>
std::sort( TwoIntsVec.begin(), TwoIntsVec.end());
// HOW TO MAKE THE SORT BASED ON the element "int a" in
TwoInts struct
}
You need to pass an appropriate comparison function to std::sort, as there is no appropriate comparison operator available for TwoInts. See overload #3 here with the description of this comparison parameter:
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns true if the first argument is less than (i.e. is ordered before) the second. [...]
One C++11 option is to pass a lambda:
std::sort( TwoIntsVec.begin(), TwoIntsVec.end(),
[](const TwoInts& lhs, const TwoInts& rhs){ return lhs.a < rhs.a; });
If you find that this requires too much typing, you can construct a predicate with Boost HOF like this:
#include <boost/hof/proj.hpp>
#include <boost/hof/placeholders.hpp>
using namespace boost::hof;
std::sort(TwoIntsVec.begin(), TwoIntsVec.end(), proj(&TwoInts::a, _ < _));
Or, as a C++20 teaser:
std::ranges::sort(TwoIntsVec, std::less<>{}, &TwoInts::a);
As a side note, I'd recommend you to fill the vector directly via
// Less complicated than doing the same thing in a function:
TwoIntsVec.push_back({21, 3});
TwoIntsVec.push_back({7, 3});
// ...
I want to use a map to count pairs of objects based on member input vectors. If there is a better data structure for this purpose, please tell me.
My program returns a list of int vectors. Each int vector is the output of a comparison between two int vectors ( a pair of int vectors). It is, however, possible, that the output of the comparison differs, though the two int vectors are the same (maybe in different order). I want to store how many different outputs (int vectors) each pair of int vectors has produced.
Assuming that I can access the int vector of my object with .inp()
Two pairs (a1,b1) and (a2,b2) should be considered equal, when (a1.inp() == a2.inp() && b2.inp() == b1.inp()) or (a1.inp() == b2.inp() and b1.inp() == a2.inp()).
This answer says:
The keys in a map a and b are equivalent by definition when neither a
< b nor b < a is true.
class SomeClass
{
vector <int> m_inputs;
public:
//constructor, setter...
vector<int> inp() {return m_inputs};
}
typedef pair < SomeClass, SomeClass > InputsPair;
typedef map < InputsPair, size_t, MyPairComparator > InputsPairCounter;
So the question is, how can I define equivalency of two pairs with a map comparator. I tried to concatenate the two vectors of a pair, but that leads to (010,1) == (01,01), which is not what I want.
struct MyPairComparator
{
bool operator() (const InputsPair & pair1, const InputsPair pair2) const
{
vector<int> itrc1 = pair1.first->inp();
vector<int> itrc2 = pair1.second->inp();
vector<int> itrc3 = pair2.first->inp();
vector<int> itrc4 = pair2.second->inp();
// ?
return itrc1 < itrc3;
}
};
I want to use a map to count pairs of input vectors. If there is a better data structure for this purpose, please tell me.
Using std::unordered_map can be considered instead due to 2 reasons:
if hash implemented properly it could be faster than std::map
you only need to implement hash and operator== instead of operator<, and operator== is trivial in this case
Details on how implement hash for std::vector can be found here. In your case possible solution could be to join both vectors into one, sort it and then use that method to calculate the hash. This is straightforward solution, but can produce to many hash collisions and lead to worse performance. To suggest better alternative would require knowledge of the data used.
As I understand, you want:
struct MyPairComparator
{
bool operator() (const InputsPair& lhs, const InputsPair pair2) const
{
return std::minmax(std::get<0>(lhs), std::get<1>(lhs))
< std::minmax(std::get<0>(rhs), std::get<1>(rhs));
}
};
we order the pair {a, b} so that a < b, then we use regular comparison.
Following the answer in this thread "What's the most efficient way to erase duplicates and sort a vector?". I wrote the following code, but I got an error complaing no match for ‘operator<’ (operand types are ‘const connector’ and ‘const connector’) blahblah...
connector is a class I wrote myself, it basically is a line with two geometry points. uniqCntrs is a std::vector. It has 100% duplicates in it, which means each element has a duplicate, the size of uniqCntrs is quite big. What's wrong with my code, and how to deal with this situation?
std::set<connector> uniqCntrsSet;
for(unsigned int i = 0; i < uniqCntrs.size(); ++i )
{
uniqCntrsSet.insert(uniqCntrs[i]);
}
uniqCntrs.assign(uniqCntrsSet.begin(), uniqCntrsSet.end());
Edit:
I have no idea how to define < operator for my connector class. I mean it is physically meaningless to say one line is smaller than the other.
From cppreference:
std::set is an associative container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function Compare.
The second template argument of std::set, Compare, is defaulted to std::less which by defaults compares the objects with operator<. To fix the issue you can simply define operator< for your Key type (connector that is).
Actually the operator< is just used to efficiently order the map which is used by std::set. It does not need to make any sense. The only requirement is that the operator satisfy the standard mathematical definition of a strict weak ordering.
Look at this point example:
class Point
{
public:
Point(int x, int y) : x(x), y(y) {
}
public:
bool operator==(const Point &other) const {
return x==other.x && y==other.y;
}
bool operator!=(const Point &other) const {
return !operator==(other);
}
bool operator<(const Point &other) const {
if (x==other.x) {
return y<other.y;
} else {
return x<other.x;
}
}
private:
int x;
int y;
};
I've a entire table stored in std::deque<record *> and I need to allow the user to sort the table on any column. The table is presented to the user in a list box format.
Each record consists of multiple strings (struct of strings). However, the fields are of different types i.e., time (HH:MM:SS), float, and strings, even though they are all stored as strings.
The user is permitted to sort on any of these columns. When the user clicks on the column, I store each record in a multimap so that the table is shown in sorted format to the user.
However, since the columns are of different types, how do I write a single compare method, that handles all these efficiently?
I thought of the following ways
Use different maps for each type and write one compare function class for each of the maps.
Use a single map, with a compare class that handles all three different types. But for each insertion, the comparison class has to decide the type , and insert accordingly.
Is there a better way than these two?
Example:
struct ltDataCompare
{
bool operator()( const CString& csData1, const CString& csData2) const
{
if ( isTimeFormat(csData1) && isTimeFormat(csData1) )
{
// Do time relevant comparision
}
else if ( isNumberFormat( csTime1 ) && isNumberFormat(csTime2) )
{
double dPrice1 = atof((LPCTSTR)csTime1);
double dPrice2 = atof((LPCTSTR)csTime2);
return ( dPrice1 < dPrice2);
}
return ( csTime1 < csTime2 );
}
};
std::multimap<CString,list_record_t*,ltDataCompare> _mapAllRecords; // Used only for sorting
You can't re-sort a map or multimap - once an item is inserted, its position is locked. It would be better to use a different container such as a vector and sort it when necessary.
The nice thing about a comparison class is that it is allowed to contain state. You can have a member with some constant or pointer to determine which comparison method to use.
You can use the same principle to choose which field to sort on.
struct ltDataCompare
{
ltDataCompare(int field, int method) : m_field(field), m_method(method) {}
bool operator()( const record& left, const record& right) const
{
if (m_method == enumTimeFormat)
return CompareTimes(left[m_field], right[m_field]);
else if (m_method == enumNumberFormat)
return CompareNumbers(left[m_field], right[m_field]);
// ...
}
int m_field;
int m_method;
};
std::sort(table.begin(), table.end(), ltDataCompare(0, enumTimeFormat));
You could be more elegant about it - I don't know you'd save yourself any work - if you had a class with a < operator in it for each of the types. If you have a superclass that has a virtual < operator then you can use it as the key type, as in
std::multimap< superclass, list_record_t >
Now you can use any of the child types as the actual keys (so long as you remain consistent).
Actually I'm not sure whether this is more clever or more elegant. More clever is generally a bad thing (as it means more obscure/less maintainable). If it makes for fewer lines of code, that's usually a good thing.