1console.log("hello-world);console.log("hello-world);.container {
width: 80%;
}<pre><code class="language-css">
.container {
width: 80%;
}
</code></pre>1console.log("hello-world);console.log("hello-world);.container {
width: 80%;
}<pre><code class="language-css">
.container {
width: 80%;
}
</code></pre>In this blogpost, we’ll be exploring the various methods sort JavaScript array case-insensitively. We’ll progress through different techniques and use cases, discuss common tips and traps, and end with a practice question to wrap up our understanding. So, get ready to get sorted, no matter what the case! 👨💻
sort() with Custom ComparatorsJavaScript’s array sort() method is incredibly versatile, especially when paired with custom comparator functions. These comparators allow for more complex sorting criteria, including case-insensitive sorting of strings. Let’s delve into two specific comparator techniques:
The localeCompare() method is a string method in JavaScript that compares two strings in the current locale. It is often used in sorting since it can handle local language conventions. By using the sensitivity option set to 'base', this method can compare strings in a case-insensitive manner.
fruits is an array of strings with varying cases.sort() method is used to arrange the items in fruits alphabetically.(a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }) is provided.a.localeCompare(b) compares two strings (a and b) according to the current locale and returns a number indicating their relative order.undefined parameter is for the locales argument, which is not needed in this case.{ sensitivity: 'base' } is an options object where sensitivity: 'base' ensures the comparison is case-insensitive but still sensitive to accents and other diacritic marks.let fruits = ["Banana", "apple", "Cherry"];
fruits.sort((a, b) =>
a.localeCompare(b, undefined, { sensitivity: "base" }),
);
console.log(fruits);
// [ 'apple', 'Banana', 'Cherry' ]This technique involves converting all strings to a uniform case (either all lowercase or uppercase) before comparing them. This ensures that the case of the strings does not affect the sorting order.
Here, the same array fruits is used.
The sort() method is again applied to sort the array.
The comparator function now converts both strings a and b to lowercase using toLowerCase() before comparing them.
a.toLowerCase().localeCompare(b.toLowerCase()) compares the lowercase versions of a and b, effectively ignoring the original case of the strings.
This ensures that the sorting is based solely on the alphabetical order of the strings, disregarding whether they were originally in uppercase or lowercase.
The final sorted array is case-insensitive, with the original case of each string preserved in the sorted output.
let fruits = ["Banana", "apple", "Cherry"];
fruits.sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase()),
);
console.log(fruits);
// [ 'apple', 'Banana', 'Cherry' ]Intl.CollatorIntl.Collator is a constructor for collators, objects that enable language-sensitive string comparison. This method is particularly useful for sorting strings in a way that adheres to specific linguistic rules. It’s more advanced than basic string comparison methods because it takes into account locale-specific rules, making it ideal for applications that require internationalization.
The example starts with an array fruits containing strings.
Intl.Collator('en', { sensitivity: 'base' }) creates a new Intl.Collator object. The first parameter, 'en', specifies the locale (English in this case). Different locales might sort strings differently, adhering to their linguistic standards.
{ sensitivity: 'base' } is an options object. The sensitivity: 'base' setting makes the comparison case-insensitive and accent-insensitive. This means ‘a’ is considered equal to ‘A’ and ‘ä’.
collator.compare is a method provided by the Intl.Collator object. This method is used as the comparator function for the sort() method.
When fruits.sort(collator.compare) is called, the array is sorted based on the criteria defined by the Intl.Collator. This results in a sorting that is appropriate for the specified locale and is insensitive to cases and accents.
This method is particularly useful for sorting strings in multilingual applications where standard ASCII-based sorting may not be sufficient.
let fruits = ['banana', 'Apple', 'cherry'];
let collator = new Intl.Collator('en', { sensitivity: 'base' });
fruits.sort(collator.compare);
console.log(fruits);
// [ 'Apple', 'banana', 'cherry' ]This technique involves comparing the ASCII values of characters. By converting all characters to the same case (upper or lower), you can compare their ASCII values for sorting, ensuring case-insensitive sorting.
The fruits array contains a mix of uppercase and lowercase strings.
In the sorting function, both a and b are converted to uppercase using toUpperCase(). This is a simple method to ensure that the comparison is case-insensitive.
The comparison a.toUpperCase() < b.toUpperCase() checks whether the ASCII value of the uppercase version of a is less than that of b. If true, it returns -1, indicating that a should come before b.
If false, it returns 1, indicating that a should come after b.
This comparison is based purely on ASCII values. In ASCII, uppercase letters have lower values than lowercase letters, so converting to a single case (either all upper or lower) ensures a fair comparison.
The result is an array sorted alphabetically in a case-insensitive manner, based purely on ASCII values.
let fruits = ['banana', 'Apple', 'cherry'];
fruits.sort((a, b) => {
return a.toUpperCase() < b.toUpperCase() ? -1 : 1;
});
console.log(fruits);
// [ 'Apple', 'banana', 'cherry' ]This method involves creating a temporary array that maps the original array elements to an object containing both the original element and a transformed version of it (e.g., the lowercase version of a string). This temporary array is then sorted based on the transformed values. Finally, the sorted order is used to arrange the original array elements.
fruits is the original array of strings.fruits.map((el, i) => ({ index: i, value: el.toLowerCase() })) creates a new array mapped. Each element of fruits is transformed into an object containing the original element’s index (i) and a lowercased version of the element (el.toLowerCase()).mapped.sort((a, b) => a.value.localeCompare(b.value)) sorts the mapped array. The sorting is based on the value property of the objects, which holds the lowercased strings. This ensures a case-insensitive sort.mapped, mapped.map(el => fruits[el.index]) is used to reconstruct the sorted array in terms of the original elements. It maps each element of mapped back to its corresponding element in fruits using the stored index.sortedFruits, which is a new array where the elements of fruits are sorted in a case-insensitive manner.let fruits = ["banana", "Apple", "cherry"];
let mapped = fruits.map((el, i) => ({
index: i,
value: el.toLowerCase(),
}));
console.log(mapped);
// [
// { index: 0, value: 'banana' },
// { index: 1, value: 'apple' },
// { index: 2, value: 'cherry' }
// ]
mapped.sort((a, b) => a.value.localeCompare(b.value));
console.log(mapped);
// [
// { index: 1, value: 'apple' },
// { index: 0, value: 'banana' },
// { index: 2, value: 'cherry' }
// ]
let sortedFruits = mapped.map((el) => fruits[el.index]);
console.log(sortedFruits);
// [ 'Apple', 'banana', 'cherry' ]This approach uses functional programming techniques for sorting. It is a more declarative way of achieving the same goal, utilizing a chain of map, sort, and map again. This method is concise and can be more readable, especially for those familiar with functional programming.
fruits array.map function fruits.map((str) => str.toLowerCase()) converts each string in the array to lowercase. This step prepares the array for case-insensitive sorting.sort() method is then called on the array of lowercased strings. This sorts the strings alphabetically, ignoring their original case.map function .map((str, i) => fruits.find(s => s.toLowerCase() === str)) transforms the sorted array of lowercased strings back into an array of the original strings. It does this by using find to locate the original string in fruits that matches the lowercased sorted string.sortedFruits, is a sorted array where the original case of each string is preserved.let fruits = ["banana", "Apple", "cherry"];
let sortedFruits = fruits
.map((str) => str.toLowerCase())
.sort()
.map((str) =>
fruits.find((s) => s.toLowerCase() === str),
);
console.log(sortedFruits);
// [ 'Apple', 'banana', 'cherry' ]By being aware of following common errors and applying these tips, you can ensure more reliable and efficient sorting of string arrays in JavaScript.
When sorting arrays in JavaScript, special attention is needed for undefined and null values. These values can lead to unexpected behaviors if not handled properly in your sorting logic.
Example with Unexpected Behavior (Not Handling undefined or null):
In following example, the sort() function is used with a comparator that converts strings to lowercase and then compares them.
However, this code does not handle null or undefined values. When toLowerCase() is called on null or undefined, it results in a TypeError, as these values do not have a toLowerCase method.
This would lead to a runtime error, causing the script to stop executing.
let fruits = ['banana', null, 'Apple', undefined, 'cherry'];
// Sorting without handling null or undefined
fruits.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
// fruits.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
// ^
// TypeError: Cannot read properties of null (reading 'toLowerCase')Example with Expected Behavior (Handling undefined or null):
In following version, the comparator function explicitly checks for null or undefined values.
If a is null or undefined, it returns 1, which will place a towards the end of the array.
If b is null or undefined, it returns -1, which will place b towards the end of the array.
Only if both a and b are non-null, non-undefined strings, the function proceeds to compare them in a case-insensitive manner using toLowerCase().
This approach prevents runtime errors and ensures that null and undefined values are sorted to the end of the array, while other strings are sorted alphabetically in a case-insensitive manner.
let fruits = ['banana', null, 'Apple', undefined, 'cherry'];
// Sorting while handling null or undefined
fruits.sort((a, b) => {
if (a == null || a == undefined) return 1;
if (b == null || b == undefined) return -1;
return a.toLowerCase().localeCompare(b.toLowerCase());
});
console.log(fruits);
// Output: ['Apple', 'banana', 'cherry', null, undefined]In JavaScript, sorting with the Array.prototype.sort() method can lead to confusion regarding whether the original array is altered. Understanding the difference between mutating and non-mutating sorting is essential for maintaining data integrity.
Mutating Example: Sorting the Original Array
sort() method is directly applied to the fruits array.sort() method in JavaScript modifies the array in place. This means the original array fruits is sorted and its order is changed after the sort operation.fruits was needed later in the program, it would no longer be available, as the array has been mutated (altered).let fruits = ["banana", "Apple", "cherry"];
// Mutating sort
fruits.sort((a, b) =>
a.localeCompare(b, undefined, { sensitivity: "base" }),
);
console.log(fruits);
// Output: ['Apple', 'banana', 'cherry']Non-Mutating Example: Preserving the Original Array
In this non-mutating approach, a copy of the fruits array is created using the spread syntax [...fruits]. This copy is then sorted.
The original array fruits remains unchanged because the sort() method is called on a copy of the array, not the original.
This approach is useful when the original order of the array needs to be preserved for later use in the program.
By sorting a copy of the array, you get the sorted results while maintaining the original array’s order.
let fruits = ["banana", "Apple", "cherry"];
// Non-mutating sort: creating a copy of the array
let sortedFruits = [...fruits].sort((a, b) =>
a.localeCompare(b, undefined, { sensitivity: "base" }),
);
console.log(sortedFruits);
// Output: ['Apple', 'banana', 'cherry']
console.log(fruits);
// Original array unchanged: ['banana', 'Apple', 'cherry']When sorting strings in JavaScript, the treatment of diacritics (like accents) can significantly affect the order. Using the localeCompare method with appropriate options allows you to control how these characters are handled.
Sorting Without Considering Accents:
localeCompare method is used with the sensitivity option set to 'base'.let fruits = ['limón', 'Lime', 'Lemon'];
// Sorting considering accents
fruits.sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'accent' }));
console.log(fruits);
// Output: ['Lemon', 'Lime', 'limón']Sorting Considering Accents
In this version, the localeCompare method is used with the sensitivity option set to 'accent'.
With this setting, the comparison is sensitive to accents but ignores case. This means ‘Lemon’ and ‘limón’ are treated as different words due to the accent on the ‘ó’.
As a result, ‘limón’ is sorted separately, acknowledging the accent difference.
let fruits = ['limón', 'Lime', 'Lemon'];
// Sorting considering accents
fruits.sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'accent' }));
console.log(fruits);
// Output: ['Lemon', 'Lime', 'limón']Sorting strings in different locales can yield varied results, particularly when using Intl.Collator, which is designed for locale-sensitive string comparison. This is crucial to understand in applications that might be used in international contexts or deal with multilingual data.
Understanding Locale-Sensitive Sorting
Using Intl.Collator: Intl.Collator is a powerful tool in JavaScript for performing string comparisons that honor these locale-specific rules. It takes into account local conventions and provides accurate sorting for different languages.
fruits contains strings that are sorted using Intl.Collator configured for the Swedish locale ('sv').sensitivity: 'base' option is used, which means that the sorting is case-insensitive but considers other distinctions between characters.let fruits = ["äpple", "banana", "apple"];
let collator = new Intl.Collator("sv", {
sensitivity: "base",
}); // Swedish locale
fruits.sort(collator.compare);
console.log(fruits);
// [ 'apple', 'banana', 'äpple' ]
// Output might vary:
// In Swedish locale, 'ä' is considered a separate letterTips for Handling Locale-Specific Sorting
Intl.Collator. This avoids any assumptions about default sorting behavior and ensures consistency across different environments.When dealing with large datasets, the efficiency of your sorting logic becomes crucial. Inefficient sorting can lead to significant performance issues, especially when using complex comparator functions.
Inefficient Example
toLowerCase() method inside the comparator function.toLowerCase() is called twice. In large arrays, this can amount to a significant number of redundant operations.Array.prototype.sort() can make multiple comparisons for each element in the array, this can lead to a substantial performance overhead.// Efficient example with a large dataset
let largeArray = [
/* large array of strings */
];
// Preprocessing step to minimize operations during sorting
let mappedArray = largeArray.map((str, index) => ({
index,
value: str.toLowerCase(),
}));
// Sort based on preprocessed values
mappedArray.sort((a, b) => a.value.localeCompare(b.value));
// Reconstruct the sorted array in the original casing
let sortedArray = mappedArray.map(
(el) => largeArray[el.index],
);
console.log(sortedArray);Efficient Example
sort() function now operates on this mapped array, comparing the preprocessed value properties. This reduces the number of operations during the sorting process.// Efficient example with a large dataset
let largeArray = [
/* large array of strings */
];
// Preprocessing step to minimize operations during sorting
let mappedArray = largeArray.map((str, index) => ({ index, value: str.toLowerCase() }));
// Sort based on preprocessed values
mappedArray.sort((a, b) => a.value.localeCompare(b.value));
// Reconstruct the sorted array in the original casing
let sortedArray = mappedArray.map(el => largeArray[el.index]);
console.log(sortedArray);Your turn now!😃 Lets test our understanding by solving a problem.
Write a JavaScript function to sort an array of mixed-case strings in a case-insensitive manner, while also maintaining the original array unsorted.
function sortCaseInsensitive(originalArray) {
// > > > 👉 Write code here 👈 < < <
}
// Testing the function
let mixedCaseFruits = ['banana', 'Apple', 'cherry', 'apricot', 'Banana'];
let sortedFruits = sortCaseInsensitive(mixedCaseFruits);
console.log('Sorted Fruits:', sortedFruits);
// Sorted Fruits: ['Apple', 'apricot', 'banana', 'Banana', 'cherry']
console.log('Original Fruits:', mixedCaseFruits);
// Original Fruits: ['banana', 'Apple', 'cherry', 'apricot', 'Banana']Please attempt before seeing the Answer:
function sortCaseInsensitive(originalArray) {
// Creating a shallow copy to avoid mutating the original array
let arrayCopy = [...originalArray];
// Using sort with a custom case-insensitive comparator
arrayCopy.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
return arrayCopy;
}Explanation:
sortCaseInsensitive takes an array originalArray as its input.[...originalArray]. This ensures that the original array is not mutated during the sorting process.sort() method is then used on the copied array with a custom comparator. The comparator uses localeCompare with the option { sensitivity: 'base' } to perform a case-insensitive comparison.In this blog post, we’ve explored various techniques to sort JavaScript array case-insensitively. From basic loops to niche use cases of locales, covering common tips & traps and a practice question to test your understanding.
Why did the array start acting weird? It heard that its future was not in order!
Hoping you are sorted, no matter what your case.😉
Keep learning, and keep coding! 🚀👨💻
Feel free to reach out!