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.

JShahi
  • 3
  • 3
  • 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. – jarmod Oct 06 '22 at 22:00

5 Answers5

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);
Rocky Sims
  • 3,523
  • 1
  • 14
  • 19
  • the beauty of JS: `files.sort((a, b) => hasExtRegex.test(a) - hasExtRegex.test(b));` – Thomas Oct 06 '22 at 22:07
  • This worked perfectly aswell, would mind explaining it to me please? – JShahi Oct 06 '22 at 22:12
  • 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. – Keith Oct 06 '22 at 22:15
  • `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. – Rocky Sims Oct 06 '22 at 22:15
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);
Keith
  • 22,005
  • 2
  • 27
  • 44
  • 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 Oct 06 '22 at 22:37
  • @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. – Keith Oct 06 '22 at 22:57
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);

Mustafa
  • 323
  • 2
  • 11
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(".")))
MrCano369x
  • 328
  • 3
  • 6
  • 1
    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. – Thomas Oct 06 '22 at 22:18
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).
Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
  • 1
    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?](https://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields) – pilchard Oct 06 '22 at 22:40
  • Which is how the OR works... (if `re.test(a)-re.test(b)` == 0 then the second part of the OR is evaluated) – pilchard Oct 06 '22 at 22:42
  • you are right, let me edit and improve – Rodrigo Rodrigues Oct 06 '22 at 22:42