johndorian
johndorian

Reputation: 41

Get ordered list of files via Javascript

I have got a folder, which contains files:

s1, s2, s3, ... s9, s10, s11, ... s99, s100, s101, s102, s103, ...,

and script, which prints a list of these files:

filesystem = require('fs');
filesystem.readdirSync('./folder').forEach(function (file) {
    console.log(file);
});

but i get a unordered list like this:

s0, s1, s10, s100, s101, s102, s103, ... s11, s110, s111, s112, s12

I know a reason, but i want to get the "number" ordered list, like this:

s1, s2, s3, ... s9, s10, s11, ... s99, s100, s101, s102, s103, ...,

Upvotes: 3

Views: 211

Answers (5)

Vala
Vala

Reputation: 5674

I feel like while some of these answers will work now, none of them are great for a general case, so I thought I'd try my hand at a more general approach that might be useful for someone else that comes along to read this question.

function getSort(caseInsensitive) {
    caseInsensitive = !!caseInsensitive;
    // Splits a string in to string and number fragments, parsing integers along the way.
    function getFragments(string) {
        var strings = string.split(/\d/);
        var numbers = string.split(/\D/);
        if (caseInsensitive === true) {
            strings = strings.map(function(string) {
                return string.toLowerCase();
            });
        }
        // Remove any empty strings (there's likely to be one at the start or at the end).
        var fragments = strings.filter(function(value) {
            return value.length > 0;
        });
        var insertIndex = 0;
        // Insert numbers in the correct place in the fragments array.
        for (var i = 0; i < numbers.length; i++) {
            if (numbers[i].length > 0) {
                fragments.splice(insertIndex, 0, parseInt(numbers[i]));
                // Add one to insert index to account for the element we just added.
                insertIndex++;
            }
            insertIndex++;
        }
        return fragments;
    }

    // Actual comparison function.
    return function(lhs, rhs) {
        var lhsFragments = getFragments(lhs);
        var rhsFragments = getFragments(rhs);

        for (var i = 0; i < lhsFragments.length; i++) {
            // Sort right-hand-side in front of left-hand-side if left-hand-side has more fragments.
            if (i >= rhsFragments.length) {
                return 1;
            }
            if (lhsFragments[i] !== rhsFragments[i]) {
                if (lhsFragments[i] < rhsFragments[i]) {
                    return -1;
                } else {
                    return 1;
                }
            }
        }
        // Sort left-hand-side in front of right-hand-side if right-hand-side has more fragments.
        if (lhsFragments.length < rhsFragments.length) {
            return -1;
        }
        return 0;
    }
}

var caseSensitiveSort = getSort();
var caseInsensitiveSort = getSort(true);
var testArray = [
    'x1',
    'X',
    'r22s1',
    'r2s2',
    's2',
    'r1t1',
    'r2',
    's1t1',
    's1',
    's1t2',
    't',
    's'
];
console.log(testArray.sort(caseSensitiveSort));
console.log(testArray.sort(caseInsensitiveSort));

Outputs:

["X", "r1t1", "r2", "r2s2", "r22s1", "s", "s1", "s1t1", "s1t2", "s2", "t", "x1"]
["r1t1", "r2", "r2s2", "r22s1", "s", "s1", "s1t1", "s1t2", "s2", "t", "X", "x1"]

It's a bit more involved of course, but not overly so. It should also handle many more cases than the answers previously posted, in particular it's not ignoring the actual string content in the comparison. Hopefully it'll be of use to someone.

Upvotes: 0

Jaffer
Jaffer

Reputation: 2968

filesystem.readdir('./folder', function(err, files) {
    files.sort(function(file1, file2) {
        var file1Int =  parseInt(file1.replace( /^\D+/g, '')),
            file2Int =  parseInt(file2.replace( /^\D+/g, ''));

        if (file1Int < file2Int) {
            return -1;
        }
        if (file1Int > file2Int) {
           return 1;
        }
        return 0;

    }).forEach(function(file) {
        console.log(file);
    });
});

Upvotes: 1

arcseldon
arcseldon

Reputation: 37105

var fs = require('fs'),
  files = fs.readdirSync('.'),
  sorted = files.sort(function (a, b) {
    return Number(a.substr(1)) > Number(b.substr(1));
  });
console.log(sorted);
// eg. [ 's0', 's1', 's10', 's100', 's101' ]

Upvotes: 0

Xlander
Xlander

Reputation: 1331

You can do something like this:

filesystem.readdirSync('./folder', function(err, files) {
    files.sort(function(a, b) {
        a = a.replace(/\w/, ""); //removing 's' or word character
        b = b.replace(/\w/, "");

        return +a - +b;
    }).forEach(function(file) {
        console.log(file);
    });
});

Upvotes: 0

CodingIntrigue
CodingIntrigue

Reputation: 78535

You can use Array.prototype.sort to sort the array by stripping off the s and parsing the rest as a number:

filesystem = require('fs');
var result = filesystem.readdirSync('./folder').sort(function (a, b) {
    return (+a.substr(1)) - (+b.substr(1));
});

Upvotes: 3

Related Questions