3

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, ...,
7
  • the folder names are not numbers, and can't be evaluated to numbers, therefore they're ordered alphabetically. However, if you have a specific pattern for naming, from which you can reliably extract a number, you can create a function that converts from string to integer, and order the list with a custom comparer. Commented Jan 12, 2016 at 12:20
  • Firstly use Async if possible ... unless readdirSync is absolutely necessary. Second, it is returning filnames sorted, just that they are sorted based on string. If you need to sort them via numbers you'll have to parse integers values from their names and then apply sorting. Commented Jan 12, 2016 at 12:20
  • I'll repeat, but shorter: Do you have a specific pattern for file name? Is it just s## for every single file you'll be working with? Commented Jan 12, 2016 at 12:25
  • if all your name starts with 's' and then a number then you can copy the object, remove the 's' and then sort according to the number. Commented Jan 12, 2016 at 12:26
  • yes, files have got s<number> pattern, so how can i do it? Commented Jan 12, 2016 at 12:31

5 Answers 5

3

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));
});
Sign up to request clarification or add additional context in comments.

Comments

1
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);
    });
});

6 Comments

That's useless in his case. The list is ordered alphabetically already.
@AlexanderMP -- true. That sort function would just return the same list.
:) Yeah, i get it now. Modified my answer to convert it to number, then sort it
Just use a different sort function that parses the filename.
The sort callback needs to return a number < 0, 0 or a number > 0. I guess there won't be two files with the same name, but still...
|
0

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);
    });
});

2 Comments

The sort callback needs to return a number < 0, 0 or a number > 0, not a Boolean.
Thanks for the notice.
0
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' ]

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.