I have a QMap<QString, myStruct>
with
myStruct {
QString firstname;
QString lastname;
QString status;
}
How can I sort this QMap
according to priority order: status
then firstname
then lastname
?
As far as I understand, you'd like to retrieve the values of the map sorted in the mentioned way, but still have access to the key. Right?
Quickly speaking, a map is a collection of <key, value>
pairs automatically sorted by key, then you may try a list of <value, key>
pairs manually sorted by value instead. Something like QList<QPair<myStruct, QString>>
, while overriding the operator<
for myStruct
.
struct myStruct {
QString firstname;
QString lastname;
QString status;
bool operator<(const myStruct& o) const {
return std::tie(status, firstname, lastname) <
std::tie(o.status, o.firstname, o.lastname);
}
};
QMap<QString, myStatus> map; // your original map
QList<QPair<myStatus, QString>> inv;
// Populate the inverted list
for (auto k : map.keys()) {
inv.append(QPair<myStatus, QString>(map[k], k));
}
std::sort(std::begin(inv), std::end(inv));
for (auto p : inv) {
qDebug() << p.first.status << p.first.firstname << p.first.lastname << p.second;
}
Of course, it is a one-time use structure that doesn't keep updated with your original map, but you mentioned that the map is fixed (constant?) so it may not be a problem then.
BTW, a QMap
can be used for the inverse look-up but only in the case the values of the myStruct
part are also unique (so they can be used also as a key), otherwise you may overwrite values when constructing the inverse map.
Note: The std::tie
is used just to simplify the sorting condition for tuples (so you'd need to include <tuple>
).
UPDATE
Answering one of your comments: Yes, you can also specify your own comparison predicate and then avoid overriding the operator<
, but I think it is harder to read and less re-usable:
std::sort(std::begin(inv), std::end(inv),
[](const QPair<myStatus, QString>& lhs, const QPair<myStatus, QString>& rhs) {
return std::tie(lhs.first.status, lhs.first.firstname, lhs.first.lastname) <
std::tie(rhs.first.status, rhs.first.firstname, rhs.first.lastname);
});
Of course, you can implement that comparison lambda as you want, I've used the std::tie
again to simplify the logic in the post. The downside is that if you need to generate the inverse map in several places you'd have to repeat the lambda expression everywhere (or create a function to create the inverse map of course).
As a side note and in case you are curious, lhs
and rhs
refers to left-hand side and right-hand side respectively, in this case they are used as lhs < rhs
by the sorting algorithm for comparing the elements.
Finally, if you'd want to avoid the std::tie
you'd have to make the comparisons manually (code below modifies the operator<
of the first version):
bool operator<(const myStruct& o) const {
if (status < o.status) return true;
if (status > o.status) return false;
// status == o.status, move to next attribute
if (firstname < o.firstname) return true;
if (firstname > o.firstname) return false;
// firstname== o.firstname, move to next attribute
if (lastname < o.lastname) return true;
if (lastname > o.lastname) return false;
return false; // are equal
}