Unsorted QVector



  • Hi,

    I have a QVector<QString>, which should store filepaths, like
    ./abc/1.jpg
    ./abc/2.jpg
    ./abc/3.jpg
    .
    .
    .
    ./abc/1209.jpg

    But instead of storing it in the above order, it's storing like
    ./abc/1.jpg
    ./abc/10.jpg
    ./abc/100.jpg
    ./abc/1000.jpg
    ./abc/1001.jpg
    .
    .

    How can I store the image filepath in numeric sequence?



  • Hi @sayan275

    Unfortunately because of the standard string compare functions you get the behavior you are seeing. The easiest way to solve this in an ideal sense is to prepend '0' to all the numbers. This is not practical or desirable. Instead, what you will need to do is write your own smart compare function that has some knowledge of your file names.

    Luckily, QVector, QList, and QMap can be used like the C++Library std::vector, std::list, and std::map.

    Here is what I am referring to:

    int my_comparator (const QString& lhs, const QString& rhs)
    {
    // Write some smart stuff to compare the files with numeric ordering
    if (lhs < rhs) return -1;
    else if (lhs > rhs) return 1;
    else return 0;
    }

    ... more code here ...

    std::sort (my_vector.begin (), my_vector.end (), my_comparator);

    Unfortunately, there is no easy way to do this except your custom function. Luckily everything you need is available. I would suggest you look online for a good test comparator that does what you want. Here is a decent article you can read about human sorting.

    https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/



  • Hello,

    This is because you are sorting strings, so 10, 100, etc.. comes before 2 in alphabet order.
    You have to convert them to numbers to sort them correctly.

    You can use std::sort() with custom "Compare" method for that.

    using lambda :

    std::sort(vector.begin(), vector.end(), [](const QString& a, const QString b) { 
        bool okA, okB;
        int ia = QFileInfo(a).baseName().toUint(&okA);
        int ib = QFileInfo(a).baseName().toUint(&okB);
       if(!okA || !okB)
            return a < b;
        return ia < ib; 
    });
    

    without lambda:

    bool compareNumberFileName(const QString& a, const QString b) { 
        bool okA, okB;
        int ia = QFileInfo(a).baseName().toUint(&okA);
        int ib = QFileInfo(a).baseName().toUint(&okB);
       if(!okA || !okB)
            return a < b;
        return ia < ib; 
    }
    
    std::sort(vector.begin(), vector.end(), compareNumberFileName);
    

    In case you are using always the same folder, you can store only numbers corresponding to file names in QVector<uint> and then construct the file name when it's necessary.


  • Lifetime Qt Champion

    Hi
    I was wondering if QStringList would just work if the goal is to just have them in the same order as inserted ?



  • @mrjj Yes it should. QList<> keep items in the order they are inserted. This is also the case of QVector<>.



  • @mrjj It's the same with QStringList. Actually the problem is with the container only which contains the qstring paths as sorted,
    "./zThumbnail/1004.jpeg"
    "./zThumbnail/1005.jpeg"
    "./zThumbnail/1006.jpeg"
    "./zThumbnail/1007.jpeg"
    "./zThumbnail/1008.jpeg"
    "./zThumbnail/1009.jpeg"
    "./zThumbnail/101.jpeg"
    "./zThumbnail/1010.jpeg"
    "./zThumbnail/1011.jpeg"
    "./zThumbnail/1012.jpeg"
    "./zThumbnail/1013.jpeg"

    QFileInfoList filelistinfo = dir.entryInfoList();
    foreach (const QFileInfo &fileinfo, filelistinfo) {
    imagePath.push_back(fileinfo.absoluteFilePath());
    qDebug() << fileinfo.absoluteFilePath();
    }



  • @Gojir4 Thanks for the suggestion.
    I constructed the filepaths.


  • Qt Champions 2018

    @sayan275 neither QVector nor QStringList are sorted!

    they are just containers that can be sorted. but if you append or push_back, than new items appear at the end.

    so you will have to sort the list yourself, as already suggested.



  • @sayan275

    With QMap, the items are always sorted by key.

    You can also use the QMap. like this:

    QFileInfoList filelistinfo = dir.entryInfoList(QStringList() << "*.jpeg");
    QMap<int, QString> map;
    for (const QFileInfo &fileinfo : qAsConst(filelistinfo))
        map[fileinfo.baseName().toInt()] = fileinfo.absoluteFilePath();
    for (QString value : qAsConst(map))
        qDebug() << value;
    

  • Moderators

    To add my 2 cents,
    you could sort twice, first along the string length than, in blocks, along the string itself.



  • I got it done by the below code

    QFileInfoList filelistinfo = dir.entryInfoList();
    for(const QFileInfo &fileinfo: filelistinfo) 
        imagePath.push_back(fileinfo.absoluteFilePath());
    
    std::sort(imagePath.begin(), imagePath.end(),
              [](const QString & a, const QString & b) -> bool
    {
        return QFileInfo(a).baseName().toInt() < QFileInfo(b).baseName().toInt();
    });
    
    qDebug()<<imagePath;
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.