I am not smart
I am not smart

Reputation: 1441

Custom string sorting

I am performing a sort on the following collection of strings using QString::localeAwareCompare:

Search term used: "Mountain"

Results:

The order is sorted lexically according to QString::compare. Ultimately I would like the order to have the following rules:

  1. Exact match at top
  2. Exact match with preceding values sorted lexically.
  3. Match contained in word sorted lexically

    • Mountain (1)
    • Mountain Goat (2)
    • Mountain Stronghold (2)
    • Mountain Titan (2)
    • Mountain Valley (2)
    • Mountain Yeti (2)
    • Goblin Mountaineer (3)
    • Madblind Mountain (3)
    • Magnetic Mountain (3)
    • Snow-Covered Mountain (3)
    • The Lady of the Mountain (3)

Does anyone know how this might be achieved? I am able to implement a custom sort of some kind.

EDIT:

Here is some janky code I have tried to get exact matches to the top, which works.

bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {

    QString leftString = sourceModel()->data(left).toString();
    QString rightString = sourceModel()->data(right).toString();

    if (leftString.compare(cardName, Qt::CaseInsensitive) == 0) {// exact match should be at top
        return true;
    }

    if (rightString.compare(cardName, Qt::CaseInsensitive) == 0) {// exact match should be at top
        return false;
    }

    return QString::localeAwareCompare(leftString, rightString) < 0;

}

Upvotes: 3

Views: 300

Answers (1)

tux3
tux3

Reputation: 7320

Here is a way you could complete your current code. It tries to sort from the most special to the most general case: exact match, match-plus-stuff, everything else.

bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, 
                               const QModelIndex &right) const {

    QString leftString = sourceModel()->data(left).toString();
    QString rightString = sourceModel()->data(right).toString();

    // The exact match (if any) should be at the top
    if (leftString.compare(cardName, Qt::CaseInsensitive) == 0)
        return true;
    if (rightString.compare(cardName, Qt::CaseInsensitive) == 0)
        return false;

    // We know that neither is the perfect match. 
    // But is either a match-plus-some-stuff ?
    bool isLeftType2 = leftString.startsWith(cardName, Qt::CaseInsensitive);
    bool isRightType2 = rightString.startsWith(cardName, Qt::CaseInsensitive);
    if (isLeftType2 && !isRightType2)
        return true;
    if (isRigthType2 && !isLeftType2)
        return false;

    // At this point we're sorting two matches of the same type
    // Either both are matches-plus-some-stuff or partial matches
    return QString::localeAwareCompare(leftString, rightString) < 0;
}

I assumed that something like "Mountaineer" alone would be type 2 and not type 3, you could add a +" " in the comparisons if you don't want that.

Upvotes: 2

Related Questions