0

I have an array called files:

['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html']

which gets listed in html. (List)

but I would like to order/sort it so everything without a ."file extentsion" gets listed at the top. Currently the browser just receives the array in alphabetical order.

What is the best way to approach this? I tried doing some research but was unable to come up with any solutions.

Thank you.

1
  • Directory names can have periods too. That aside, one simple solution is to split the array into two arrays, one with periods, one without. You can use filter to do this. Sort the two arrays independently, then combine. Commented Oct 6, 2022 at 22:00

5 Answers 5

1

const files = ['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html'];

const hasExtRegex = /\.[a-z0-9]{1,4}$/;
const sortedFiles = files.sort((a, b) => {
  const aHasExt = hasExtRegex.test(a);
  const bHasExt = hasExtRegex.test(b);
  return aHasExt - bHasExt;
});

console.log(sortedFiles);

Sign up to request clarification or add additional context in comments.

4 Comments

the beauty of JS: files.sort((a, b) => hasExtRegex.test(a) - hasExtRegex.test(b));
This worked perfectly aswell, would mind explaining it to me please?
Just be aware if text ordering is important, this is using something called stable sort, IOW: If the source array is not already sorted, this would only sort the truthiness of the period, and the text would still remain in the original order, sorted or not. This might be fine, but might be worth pointing out.
hasExtRegex.test(a) will return 0 if a doesn't end with an extension else it will return 1. By doing aHasExt - bHasExt we get a number (-1, 0, or 1) which tells sort() which of the 2 elements (a or b) should come first. To understand the regex, go to regexr.com and paste in /\.[a-z0-9]{1,4}$/ as the regular expression.
0

The way I do this is to create a score system, So for example in your case if we have a period I would prefix with a 1, if we don't I prefix with a 0, this will make the sort place the 0 prefix score's at the top.

eg..

const arr = ['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html'];

const score = a => `${a.includes('.')?'1':'0'}${a}`

arr.sort((a,b) => {
  return score(a).localeCompare(
    score(b), undefined, {sensitivity: 'base'});
});


console.log(arr);

2 Comments

better than changing the string would simply be to sort first by includes(.) and then lexicographically. return a.includes('.') - b.includes('.') || a.localeCompare(b)
@pilchard Yes, that's what I used to do, but generally I now do this scoring system out of habit. It just feels more flexible, if later you wanted to sort on other props etc, it gets less confusing. eg, if for some odd reason filenames with a - I wanted at the end, I could just give that a score of 2 etc etc. With a little bit of imagination you can pretty much create a score for anything by making an adjusted string, even if using numbers.. etc.
0

// a longer answer but this is without using sort of javascript

const values = ['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html'];
let j = 1;
let i = 0;
while(i < values.length) {
    
    if(j >= values.length) break;
    if(values[i].includes(".")) {
        i++;
        j++;
        continue;
    }
    
    if(!values[i].includes(".") && values[j].includes(".")) {
        // swap
        let temp = values[i];
        values[i] = values[j];
        values[j] = temp;
        continue;
    }
    
    if(!values[i].includes(".") && !values[j].includes(".")) {
        j++;
        continue;
    }
};

console.log(values);

2 Comments

But why? Why do you reinvent the wheel?
You are correct, but the logic of this solution can be applied in any programming language. especially ones that don't have sort like js
0

this is short, although this loses the alphabetical sorting

const arr = ['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html'];

arr.sort((a) => a.includes(".") ? 1 : -1 );

console.log(arr);

Note that sort mutates the array, a solution without sort would be:

const newArr = arr.filter(a => !a.includes(".")).concat(arr.filter(a => a.includes(".")))

1 Comment

This is not how the sort-function works. Depending on which sort-algorithm the JS engine decides to use this code may produce almost random results.
0

This works even if your list is not originally sorted, and it only sorts once:

names = ['Cpp-New.html', 'dirname', 'dirname.html', 'dirname.py', 'HarryPotter', 'Java-New.html', 'poop.css', 'test01.html', 'Web-New.html']
re = /.*\..+$/ 
names.sort((a, b) => re.test(a)-re.test(b) || a.localeCompare(b)) 

Explaining:

  • re.test(a)-re.test(b) will give 0 (false) if both a and b either have or don't have extension, then will execute the expression after ||. Otherwise, will shortcircuit and just decide the sorting based on the extesion.
  • a.localeCompare(b) will compare a and b alphabetically (ignore case and accents).

3 Comments

Don't do weird arithmetic, just use an OR short circuit, re.test(a)-re.test(b) || a.localeCompare(b) see: How to sort an array of objects by multiple fields?
Which is how the OR works... (if re.test(a)-re.test(b) == 0 then the second part of the OR is evaluated)
you are right, let me edit and improve

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.