How to Sort JavaScript Array Case-Insensitively


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! 👨‍💻


Ways to sort JavaScript array case-insensitively

Using Built-in sort() with Custom Comparators

JavaScript’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:

Basic Comparator with localeCompare():

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.

  • In following example, fruits is an array of strings with varying cases.
  • The sort() method is used to arrange the items in fruits alphabetically.
  • For the sorting logic, a custom comparator function (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.
  • The 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.
  • The result is a sorted array where the case of the letters is ignored.
Basic Comparator with localeCompare() (JavaScript)
let fruits = ["Banana", "apple", "Cherry"];

fruits.sort((a, b) =>
  a.localeCompare(b, undefined, { sensitivity: "base" }),
);

console.log(fruits);
// [ 'apple', 'Banana', 'Cherry' ]

Comparator with toLowerCase() or toUpperCase()

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.
Comparator with toLowerCase() or toUpperCase() (JavaScript)
let fruits = ["Banana", "apple", "Cherry"];

fruits.sort((a, b) =>
  a.toLowerCase().localeCompare(b.toLowerCase()),
);

console.log(fruits);
// [ 'apple', 'Banana', 'Cherry' ]

Advanced Locale-Sensitive Sorting with Intl.Collator

Intl.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.
Locale-Sensitive Sorting with Intl.Collator (JavaScript)
let fruits = ['banana', 'Apple', 'cherry'];
let collator = new Intl.Collator('en', { sensitivity: 'base' });

fruits.sort(collator.compare);
console.log(fruits);
// [ 'Apple', 'banana', 'cherry' ]

Sorting with ASCII Value Comparisons

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.
ASCII Value Comparisons (JavaScript)
let fruits = ['banana', 'Apple', 'cherry'];

fruits.sort((a, b) => {
  return a.toUpperCase() < b.toUpperCase() ? -1 : 1;
});

console.log(fruits);
// [ 'Apple', 'banana', 'cherry' ]

Using Temporary Arrays and Mapping

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.
  • After sorting 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.
  • The result is sortedFruits, which is a new array where the elements of fruits are sorted in a case-insensitive manner.
Temporary Arrays and Mapping (JavaScript)
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' ]

Functional Programming Approach

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.

  • The process starts with the same fruits array.
  • The first map function fruits.map((str) => str.toLowerCase()) converts each string in the array to lowercase. This step prepares the array for case-insensitive sorting.
  • The sort() method is then called on the array of lowercased strings. This sorts the strings alphabetically, ignoring their original case.
  • Finally, the second 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.
  • The result, sortedFruits, is a sorted array where the original case of each string is preserved.
Functional Programming Approach (JavaScript)
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' ]

Common Errors and Tips to Sort JavaScript Array Case-Insensitively

By being aware of following common errors and applying these tips, you can ensure more reliable and efficient sorting of string arrays in JavaScript.

Handling undefined and null Values in JavaScript Sorting

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.
Not Handling undefined or null (JavaScript)
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.
Handling undefined or null (JavaScript)
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]

Mutating vs. Non-Mutating Sorting in JavaScript

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

  • In following example, the sort() method is directly applied to the fruits array.
  • The 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.
  • As a result, if the original order of fruits was needed later in the program, it would no longer be available, as the array has been mutated (altered).
Mutating Example (JavaScript)
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.
Non-Mutating Example (JavaScript)
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']

Handling Diacritics and Accents in JavaScript Sorting

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:

  • Here, the localeCompare method is used with the sensitivity option set to 'base'.
  • This setting means that only the base letter is considered, ignoring case and diacritics. So, ‘Lemon’ and ‘limón’ are considered equivalent in terms of sorting, as the accent on the ‘ó’ is ignored.
  • The result is that ‘Lemon’ and ‘limón’ are sorted as if they were the same base word, with case also being ignored.
Without Considering Accents (JavaScript)
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.
Considering Accents (JavaScript)
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']

Inconsistent Results Across Different Locales

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

  • Different languages and regions have unique rules for sorting strings. For instance, certain characters or accents that are distinct in one language might be considered equivalent in another.
  • The order of characters like ‘ä’, ‘ö’, ‘ü’, etc., can significantly vary between locales. In some languages, these are treated as variations of ‘a’, ‘o’, ‘u’, whereas in others, they are considered separate letters.

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.

  • In following example, fruits contains strings that are sorted using Intl.Collator configured for the Swedish locale ('sv').
  • The sensitivity: 'base' option is used, which means that the sorting is case-insensitive but considers other distinctions between characters.
  • In Swedish (‘sv’), the character ‘ä’ is treated as a separate letter and not as a variant of ‘a’. This affects its position in the sorted list.
  • Therefore, when sorted according to Swedish rules, ‘äpple’ might be placed differently compared to sorting according to English rules.
Using Intl.Collator (JavaScript)
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 letter

Tips for Handling Locale-Specific Sorting

  • Explicitly Specify Locale: Always specify the locale when using Intl.Collator. This avoids any assumptions about default sorting behavior and ensures consistency across different environments.
  • Understand the Locale’s Sorting Rules: Be aware of how specific locales treat certain characters, especially if your application deals with international data.
  • Test with Multiple Locales: If your application is intended for a global audience, test the sorting functionality across different locales to ensure it behaves as expected.

Performance Issues with Case-Insensitively Sorting Large JavaScript Arrays

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

  • This example uses the toLowerCase() method inside the comparator function.
  • For every comparison made by the sort, toLowerCase() is called twice. In large arrays, this can amount to a significant number of redundant operations.
  • Since the sorting algorithm used by Array.prototype.sort() can make multiple comparisons for each element in the array, this can lead to a substantial performance overhead.
Inefficient Example (JavaScript)
// 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

  • In this approach, a preprocessing step is added where each string is converted to lowercase and paired with its original index. This reduces the case conversion operation to just once per element, regardless of the sorting algorithm’s complexity.
  • The sort() function now operates on this mapped array, comparing the preprocessed value properties. This reduces the number of operations during the sorting process.
  • Finally, the original array is reconstructed in its sorted order using the index stored in the mapped array. This ensures the original casing is preserved.
  • By reducing the complexity within the comparator function and performing case conversion only once per element, this method significantly improves performance, especially noticeable in large arrays.
Efficient Example (JavaScript)
// 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);

🧪Practice Coding Problem

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.

Problem (JavaScript)
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:
Problem (JavaScript)
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:

  • The function sortCaseInsensitive takes an array originalArray as its input.
  • Inside the function, a copy of the original array is created using the spread syntax [...originalArray]. This ensures that the original array is not mutated during the sorting process.
  • The 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.
  • Finally, the function returns the sorted copy of the array, leaving the original array intact.

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! 🚀👨‍💻

Scroll to Top