How do you sort strings in this manner?

This is how qBittorrent, a BitTorrent client, sorts names of files.

qBittorrent’s file sorting Disclaimer: I’m not interested in the torrent and the files, don’t know what those are, and haven’t downloaded any one of those.

How do you sort strings in Javascript in this manner? I don’t quite get the logic, but it is certainly not random.

I tried this

strings.sort((a, b) => a.localeCompare(b, "en", {
    sensitivity: "case",
    caseFirst: "upper",
    numeric: true
}));

But this was what I got, which is not what I intended

[
    "1.ico",
    "01.ico",
    "03.ico",
    "04.ico",
    "05.ico",
    "7-zip ICO m1losh_1.ico",
    "07.ico",
    "7z-silver.ico",
    "7z.ico",
    "7z1.ico",
    "7zfm.ico",
    "7Zip.ico",
    "7ZSFX.ico",
    "7zSfxCreate_99.ico",
    "7ZSplit.ico",
    "7zz.ico",
    "08.ico",
    "09.ico",
    "11.ico",
    "14.ico",
    "22.ico",
    "101.ico",
    "107.ico",
    "128.ico",
    "138.ico",
    "777.ico",
    "7833-ZeDudeMan-WinRar_vista.ico",
    "exe.ico",
    "hfs.ico",
    "hg.ico",
    "Icon.ico",
    "Icon1.ico",
    "icon17.ico",
    "inst.ico",
    "Install-2.ico",
    "Install-3.ico",
    "Install-4.ico",
    "Install-5.ico",
    "Install-6.ico",
    "Install-7.ico",
    "Install-8.ico",
    "Install-9.ico",
    "Install-11.ico",
    "Install-12.ico",
    "Install-13.ico",
    "Install-14.ico",
    "Install-15.ico",
    "Install-16.ico",
    "Install-17.ico",
    "Install-18.ico",
    "MAINICON.ico",
    "momitor.ico",
    "PSA_1.ico",
    "qfInstall.ico",
    "Readme!!!.txt",
    "Token_7zip-dark.ico",
    "Token_7zip-light.ico",
    "wusa.ico"
]

Upvotes: 2

Views: 99

Answers (2)

adiga
adiga

Reputation: 35222

Sort based on:

  • Whether the item starts with a number
  • Then, based on the number of characters in the leading number
  • Then, do a natural sort

const array=["1.ico","01.ico","03.ico","04.ico","05.ico","7-zip ICO m1losh_1.ico","07.ico","7z-silver.ico","7z.ico","7z1.ico","7zfm.ico","7Zip.ico","7ZSFX.ico","7zSfxCreate_99.ico","7ZSplit.ico","7zz.ico","08.ico","09.ico","11.ico","14.ico","22.ico","101.ico","107.ico","128.ico","138.ico","777.ico","7833-ZeDudeMan-WinRar_vista.ico","exe.ico","hfs.ico","hg.ico","Icon.ico","Icon1.ico","icon17.ico","inst.ico","Install-2.ico","Install-3.ico","Install-4.ico","Install-5.ico","Install-6.ico","Install-7.ico","Install-8.ico","Install-9.ico","Install-11.ico","Install-12.ico","Install-13.ico","Install-14.ico","Install-15.ico","Install-16.ico","Install-17.ico","Install-18.ico","MAINICON.ico","momitor.ico","PSA_1.ico","qfInstall.ico","Readme!!!.txt","Token_7zip-dark.ico","Token_7zip-light.ico","wusa.ico"];

array.sort((a, b) => {
    const n1 = a.match(/^\d+/)?.[0],
          n2 = b.match(/^\d+/)?.[0]

    return (n1 === null) - (n2 === null)
           || n1?.length - n2?.length
           || a.localeCompare(b, undefined, {numeric: true, sensitivity: 'base'})
})

console.log(array)

Upvotes: 3

Modified @adiga and @Chris G’s code[a.][C.] a bit.

const strings = ["1.ico","01.ico","03.ico","04.ico","05.ico","7-zip ICO m1losh_1.ico","07.ico","7z-silver.ico","7z.ico","7z1.ico","7zfm.ico","7Zip.ico","7ZSFX.ico","7zSfxCreate_99.ico","7ZSplit.ico","7zz.ico","08.ico","09.ico","11.ico","14.ico","22.ico","101.ico","107.ico","128.ico","138.ico","777.ico","7833-ZeDudeMan-WinRar_vista.ico","exe.ico","hfs.ico","hg.ico","Icon.ico","Icon1.ico","icon17.ico","inst.ico","Install-2.ico","Install-3.ico","Install-4.ico","Install-5.ico","Install-6.ico","Install-7.ico","Install-8.ico","Install-9.ico","Install-11.ico","Install-12.ico","Install-13.ico","Install-14.ico","Install-15.ico","Install-16.ico","Install-17.ico","Install-18.ico","MAINICON.ico","momitor.ico","PSA_1.ico","qfInstall.ico","Readme!!!.txt","Token_7zip-dark.ico","Token_7zip-light.ico","wusa.ico"];

strings.sort((a, b) =>
    ((a.match(/^\d+/)?.[0].length || Infinity) - (b.match(/^\d+/)?.[0].length || Infinity))
        || a.localeCompare(b, "en-u-kf-upper-kn", {sensitivity: "base"})
);

console.log(strings);

Upvotes: 0

Related Questions