I have an array of strings I need to sort in JavaScript, but in a case-insensitive way. How to perform this?
15 Answers
In a one-liner
["Foo", "bar"].sort((a, b) => a.localeCompare(b, 'en', {'sensitivity': 'base'}));
Which results in
[ 'bar', 'Foo' ]
Replace 'en' with your desired locale if needed, see method reference and more information. Method widely available since 2017.
Important note from MDN:
When comparing large numbers of strings, such as in sorting large arrays, it is better to create an Intl.Collator object and use the function provided by its compare() method.
Example:
const collator = new Intl.Collator('en'); // replace 'en' with your desired locale if needed
["Foo", "bar"].sort(collator.compare)
Also widely available since 2017. Remember to reuse the collator, instead of creating new every time.
While simply
["Foo", "bar"].sort();
results in
[ 'Foo', 'bar' ]
10 Comments
return a.localeCompare(b, 'en', {'sensitivity': 'base'});toLowerCase() when localeCompare already does that by default in some cases. You can read more about the parameters to pass to it here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…items.sort(new Intl.Collator('en').compare) for better performance. (See MDN.)It is time to revisit this old question.
You should not use solutions relying on toLowerCase. They are inefficient and simply don't work in some languages (Turkish for instance). Prefer this:
['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))
Check the documentation for browser compatibility and all there is to know about the sensitivity option.
5 Comments
['Foo', 'bar'].sort((a,b) => a.localeCompare(b)) also worksmyArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
EDIT: Please note that I originally wrote this to illustrate the technique rather than having performance in mind. Please also refer to answer @Ivan Krechetov for a more compact solution.
3 Comments
toLowerCase twice on each string; would be more efficient to stored lowered versions of the string in variables..toLowerCase() multiple times for each item in array. For example, 45 calls to the compare function when sorting 10 items in reverse order. var i = 0; ["z","y","x","w","v","u","t","s","r","q"].sort(function (a, b) {++i; return a.toLowerCase().localeCompare(b.toLowerCase());}); console.log("Calls to Compare: " + i); // i === 45ES6 version:
["Foo", "bar"].sort(Intl.Collator().compare)
Comments
arr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a == b) return 0;
if (a > b) return 1;
return -1;
});
3 Comments
return a === b ? 0 : a > b ? 1 : -1;["111", "33"], we might want it to return ["111", "33"] because 1 comes before 3 in character code ordering. However, the function in this answer will return ["33", "111"] because the number 33 is less than the number 111."33" > "111" === true and 33 > 111 === false. It works as intended.You can also use the new Intl.Collator().compare, per MDN it's more efficient when sorting arrays. The downside is that it's not supported by older browsers. MDN states that it's not supported at all in Safari. Need to verify it, since it states that Intl.Collator is supported.
When comparing large numbers of strings, such as in sorting large arrays, it is better to create an Intl.Collator object and use the function provided by its compare property
["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]
Comments
If you want to guarantee the same order regardless of the order of elements in the input array, here is a stable sorting:
myArray.sort(function(a, b) {
/* Storing case insensitive comparison */
var comparison = a.toLowerCase().localeCompare(b.toLowerCase());
/* If strings are equal in case insensitive comparison */
if (comparison === 0) {
/* Return case sensitive comparison instead */
return a.localeCompare(b);
}
/* Otherwise return result */
return comparison;
});
Comments
You can also use the Elvis operator:
arr = ['Bob', 'charley', 'fudge', 'Fudge', 'biscuit'];
arr.sort(function(s1, s2){
var l=s1.toLowerCase(), m=s2.toLowerCase();
return l===m?0:l>m?1:-1;
});
console.log(arr);
Gives:
biscuit,Bob,charley,fudge,Fudge
The localeCompare method is probably fine though...
Note: The Elvis operator is a short form 'ternary operator' for if then else, usually with assignment.
If you look at the ?: sideways, it looks like Elvis...
i.e. instead of:
if (y) {
x = 1;
} else {
x = 2;
}
you can use:
x = y?1:2;
i.e. when y is true, then return 1 (for assignment to x), otherwise return 2 (for assignment to x).
1 Comment
x = y ? y : z, you can do x = y ?: z. Javascript doesn't have an actual Elvis operator, but you can use x = y || z in a similar fashion.The other answers assume that the array contains strings. My method is better, because it will work even if the array contains null, undefined, or other non-strings.
var notdefined;
var myarray = ['a', 'c', null, notdefined, 'nulk', 'BYE', 'nulm'];
myarray.sort(ignoreCase);
alert(JSON.stringify(myarray)); // show the result
function ignoreCase(a,b) {
return (''+a).toUpperCase() < (''+b).toUpperCase() ? -1 : 1;
}
The null will be sorted between 'nulk' and 'nulm'. But the undefined will be always sorted last.
6 Comments
(''+notdefined) === "undefined" so it'd sort before "z"Array.prototype.sort :| because the part about (''+notdefined) === "undefined" really is true... which means if you flip the -1 and 1 in the sort function to reverse the order, undefined still sorts to the end. It also needs to be considered when using the comparison function outside the context of an array sort (as I was when I came upon this question).Array.prototype.sort definition - couple more comments. First, there's no need for the (''+a) - ECMAScript requires toString() to be called on elements prior to passing them into compareFn. Second, the fact that ignoreCase returns 1 when comparing equal (including equal-but-for-case) strings means the specification doesn't define the result if there are duplicate values (will probably be fine just with some unnecessary swaps occurring, I think).undefined is a special case, which for any x x<undefined and x>undefined are both false. That undefined is always last, is a byproduct of the sort implementation of sort. I tried to change the (''+a) to simply a, but it fails. i get TypeError: a.toUpperCase is not a function. Apparently toString is not called prior to calling compareFn.undefined the compareFn is never calledarr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if( a == b) return 0;
if( a > b) return 1;
return -1;
});
In above function, if we just compare when lower case two value a and b, we will not have the pretty result.
Example, if array is [A, a, B, b, c, C, D, d, e, E] and we use the above function, we have exactly that array. It's not changed anything.
To have the result is [A, a, B, b, C, c, D, d, E, e], we should compare again when two lower case value is equal:
function caseInsensitiveComparator(valueA, valueB) {
var valueALowerCase = valueA.toLowerCase();
var valueBLowerCase = valueB.toLowerCase();
if (valueALowerCase < valueBLowerCase) {
return -1;
} else if (valueALowerCase > valueBLowerCase) {
return 1;
} else { //valueALowerCase === valueBLowerCase
if (valueA < valueB) {
return -1;
} else if (valueA > valueB) {
return 1;
} else {
return 0;
}
}
}
Comments
In support of the accepted answer I would like to add that the function below seems to change the values in the original array to be sorted so that not only will it sort lower case but upper case values will also be changed to lower case. This is a problem for me because even though I wish to see Mary next to mary, I do not wish that the case of the first value Mary be changed to lower case.
myArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
In my experiments, the following function from the accepted answer sorts correctly but does not change the values.
["Foo", "bar"].sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
1 Comment
This may help if you have struggled to understand:
var array = ["sort", "Me", "alphabetically", "But", "Ignore", "case"];
console.log('Unordered array ---', array, '------------');
array.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
console.log("Compare '" + a + "' and '" + b + "'");
if( a == b) {
console.log('Comparison result, 0 --- leave as is ');
return 0;
}
if( a > b) {
console.log('Comparison result, 1 --- move '+b+' to before '+a+' ');
return 1;
}
console.log('Comparison result, -1 --- move '+a+' to before '+b+' ');
return -1;
});
console.log('Ordered array ---', array, '------------');
// return logic
/***
If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
***/
Comments
I wrapped the top answer in a polyfill so I can call .sortIgnoreCase() on string arrays
// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
Array.prototype.sortIgnoreCase = function () {
return this.sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
};
}
1 Comment
Wrap your strings in / /i. This is an easy way to use regex to ignore casing