Reputation: 91
So I was wandering around the internet searching for some sorting function in js. Here is the problem. We have a string array like this :
['único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL']
and we want somthing like this (Uppercase first):
['ARBOL', 'COSAS', 'FUTBOL', 'UNICO', 'árbol', 'cosas', 'fútbol', 'único']
or like this (lowercase first):
['árbol', 'cosas', 'fútbol', 'único', 'ARBOL', 'COSAS', 'FUTBOL', 'UNICO']
The thing is : it's really easy to get this :
['ARBOL', 'COSAS', 'FUTBOL', 'UNICO', 'cosas', 'fútbol', 'árbol','único']
with the .sort();
function but we don't want the accentuated words at the end so we use the
.sort(function(a, b) {
return a.localCompare(b);
});
but we end up with this ...
['ARBOL', 'árbol', 'COSAS', 'cosas', 'FUTBOL', 'fútbol', 'UNICO', 'único']
Do you guys have any idea on how to combine both ?
Upvotes: 7
Views: 4064
Reputation: 386654
You could take a sorting with map and replace every character with two characters, depending on the case with a space in front or behind of the caracter. Then sort and map a new array.
Example
unsorted
index value ----- -------------------- 0 'ú n i c o ' 1 ' U N I C O' 2 'á r b o l ' 3 ' A R B O L' 4 ' c o s a s ' 5 ' C O S A S' 6 ' f ú t b o l ' 7 ' F U T B O L'
sorted
index value ----- -------------------- 3 ' A R B O L' 5 ' C O S A S' 7 ' F U T B O L' 1 ' U N I C O' 4 ' c o s a s ' 6 ' f ú t b o l ' 2 'á r b o l ' 0 'ú n i c o '
var array = ['único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL'],
result = array
.map((s, index) => ({ index, value: Array.from(s, c => c === c.toUpperCase()
? ' ' + c
: c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
? ' ' + c + ' '
: c + ' '
).join('') }))
.sort((a, b) => a.value.localeCompare(b.value))
.map(({ index }) => array[index]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 191996
Sort the array, and if:
a
are both uppercase or both lowercase, sort them via localeCompare.a
is uppercase a should be 1st (return -1
).b
is uppercase) is b
first (return 1).Note: this assumes that all words don't have mixed uppercase and lowercase letters.
const arr = ['único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL']
arr.sort((a, b) => {
const aIsUpperCase = a.toUpperCase() === a;
const bIsUpperCase = b.toUpperCase() === b;
if(aIsUpperCase === bIsUpperCase) return a.localeCompare(b, 'es');
if(aIsUpperCase) return -1;
return 1;
})
console.log(arr)
Upvotes: 2
Reputation: 73241
You could split them by case, sort each and put back together.
let foo = ['único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL'];
foo = foo.reduce((a, b) => {
b.toUpperCase() === b ? a[0].push(b) : a[1].push(b);
return a;
}, [[],[]]).flatMap(e => e.sort((a, b) => a.localeCompare(b)));
console.log(foo);
Upvotes: 0
Reputation: 5815
You can simply extend the sort function to prioritize uppercase characters in the beginning of strings:
const arr = ['ÁRBOL', 'único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL'];
function startsWithUppercase(str) {
return str.substr(0, 1).match(/[A-Z\u00C0-\u00DC]/);
}
arr.sort(function(a, b) {
if (startsWithUppercase(a) && !startsWithUppercase(b)) {
return -1;
} else if (startsWithUppercase(b) && !startsWithUppercase(a)) {
return 1;
}
return a.localeCompare(b);
});
console.log(arr);
Upvotes: 5
Reputation: 4097
I don't believe it's possible with localeCompare
alone, see:
How to get localeCompare to behave similarly to .sort(), so that all capital letters come first?:
But you can combine the method described here with sort
:
const arr = ['único', 'UNICO', 'árbol', 'ARBOL', 'cosas', 'COSAS', 'fútbol', 'FUTBOL'];
const norm = str => str.normalize('NFD').replace(/[\u0300-\u036f]/g, "")
arr.sort((a, b) => Number(norm(a) > norm(b)) || -(Number(norm(b) > norm(a))));
console.log(arr);
// ['ARBOL', 'COSAS', FUTBOL', 'UNICO', 'árbol', 'cosas', 'fútbol', 'único']
Upvotes: 3