diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 3af72ea3e..776912aaa 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -19,6 +19,7 @@ #include "blackmisc/json.h" #include "blackmisc/metaclass.h" #include "blackmisc/stringutils.h" +#include "blackmisc/typetraits.h" #include "blackmisc/variant.h" #include @@ -29,6 +30,31 @@ namespace BlackMisc { + class CPropertyIndex; + + namespace Private + { + //! \private + template + int compareByProperty(const T &a, const T &b, const CPropertyIndex &index, std::true_type, X) + { + return a.comparePropertyByIndex(index, b); + } + //! \private + template + int compareByProperty(const T &a, const T &b, const CPropertyIndex &index, std::false_type, std::true_type) + { + return compare(a.propertyByIndex(index), b.propertyByIndex(index)); + } + //! \private + template + int compareByProperty(const T &, const T &, const CPropertyIndex &, std::false_type, std::false_type) + { + qFatal("Not implemented"); + return 0; + } + } + /*! * Property index. The index can be nested, that's why it is a sequence * (e.g. PropertyIndexPilot, PropertyIndexRealname). @@ -175,6 +201,16 @@ namespace BlackMisc return static_cast(ev) == l.first(); } + //! Return a predicate function which can compare two objects based on this index + auto comparator() const + { + return [index = *this](const auto &a, const auto &b) + { + using T = std::decay_t; + return Private::compareByProperty(a, b, index, HasCompareByPropertyIndex(), HasPropertyByIndex()); + }; + } + //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 9c386cabb..f17673b4d 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -14,6 +14,7 @@ #include "iterator.h" #include "containerbase.h" +#include "propertyindex.h" #include "icon.h" #include #include @@ -387,6 +388,21 @@ namespace BlackMisc sort(BlackMisc::Predicates::MemberLess(key1, keys...)); } + //! In-place sort by some properties specified by a list of property indexes. + void sortByProperty(const CSequence &indexes) + { + sort([&indexes](const T &a, const T &b) + { + for (const auto &index : indexes) + { + int cmp = index.comparator()(a, b); + if (cmp < 0) { return true; } + if (cmp > 0) { return false; } + } + return false; + }); + } + //! Return a copy sorted by a given comparator predicate. template CSequence sorted(Predicate p) const @@ -405,6 +421,14 @@ namespace BlackMisc return sorted(BlackMisc::Predicates::MemberLess(key1, keys...)); } + //! Return a copy sorted by some properties specified by a list of property indexes. + CSequence sortedByProperty(const CSequence &indexes) const + { + CSequence result = *this; + result.sortByProperty(indexes); + return result; + } + //! In-place move the smallest n elements to the beginning and sort them. template void partiallySort(size_type n, Predicate p) { diff --git a/src/blackmisc/typetraits.h b/src/blackmisc/typetraits.h index 44f3d3d4d..210e2e2b5 100644 --- a/src/blackmisc/typetraits.h +++ b/src/blackmisc/typetraits.h @@ -19,6 +19,8 @@ namespace BlackMisc { + class CPropertyIndex; + //! \cond PRIVATE #ifdef BLACK_HAS_FIXED_CWG1558 // Own implementation of C++17 std::void_t, simple variadic alias @@ -102,6 +104,28 @@ namespace BlackMisc struct HasCompare(), std::declval()))>> : public std::true_type {}; //! \endcond + /*! + * Trait which is true if the expression a.compareByPropertyIndex(b, i) is valid when a and b are instances of T, + * and i is an instance of CPropertyIndex. + */ + template > + struct HasCompareByPropertyIndex : public std::false_type {}; + //! \cond + template + struct HasCompareByPropertyIndex().compareByPropertyIndex(std::declval(), std::declval()))>> : public std::true_type {}; + //! \endcond + + /*! + * Trait which is true if the expression a.propertyByIndex(i) is valid with a is an instance of T and i is an + * instance of CPropertyIndex. + */ + template > + struct HasPropertyByIndex : public std::false_type {}; + //! \cond + template + struct HasPropertyByIndex().propertyByIndex(std::declval()))>> : public std::true_type {}; + //! \endcond + /*! * Trait which is true if the expression a == b is valid when a and b are instances of T and U. */