Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can the Lodash orderBy function be made to work with accented characters?

Is there a way to make Lodash's orderBy function support accented characters?

Like á, é, ñ, etc. These are moved to the end of the array when the sort is performed.

like image 474
Omar Cardona Avatar asked Jun 24 '17 20:06

Omar Cardona


People also ask

How does Lodash orderBy work?

orderBy() method is similar to _. sortBy() method except that it allows the sort orders of the iterates to sort by. If orders are unspecified, then all values are sorted in ascending order otherwise order of corresponding values specifies an order of “desc” for descending or “asc” for ascending sort.

How do you sort an array using Lodash?

Lodash helps in working with arrays, strings, objects, numbers, etc. The Loadsh. sortBy() function is used to sort the array in ascending order. Parameters: This parameter holds the collection as a first parameter, Second parameter is optional.

Is Lodash unique?

'uniq' The Lodash uniq method creates an array without duplicate values. Only the first occurrence of each value appears in the returned array. Since JavaScript already has the set data structure, we can use that with the spread operator to remove all the duplicates from the original array and return it.

Is Lodash a string?

The _. isString() method is used to find whether the given value is a string object or not. It returns True if the given value is a string. Otherwise, it returns false.


1 Answers

The Problem

It sounds like it doesn't use localeCompare, defaulting instead to the equivalent of using < or >, which compares by UTF-16 code unit numeric values, not locale-aware collation (ordering).

Controlling Comparison Method

You can convert to array (if needed) and then use the native sort with localeCompare. For instance, instead of:

const result = _.orderBy(theArray, ["value"]);

you can do:

const result = theArray.slice().sort((a, b) => a.value.localeCompare(b.value));

or to sort in-place:

theArray.sort((a, b) => a.value.localeCompare(b.value));

localeCompare uses the default collation order for the default locale. Using Intl.Collator, you can have more control over the collation (like case-insensitivity, the handling of accents, the relative position of upper- and lower-case characters, etc.). For instance, if you wanted the default collation for the default locale but with upper-case characters first:

const collator = new Intl.Collator(undefined, {caseFirst: "upper"});
const result = theArray.slice().sort((a, b) => collator.compare(a.value, b.value));

Live Example:

const theArray = [
    {value: "n"},
    {value: "N"},
    {value: "ñ"},
    {value: "á"},
    {value: "A"},
    {value: "a"},
];
const lodashResult = _.orderBy(theArray, ["value"]);
const localeCompareResult = theArray.slice().sort((a, b) => a.value.localeCompare(b.value));
const collator = new Intl.Collator(undefined, {caseFirst: "upper"});
const collatorResult = theArray.slice().sort((a, b) => collator.compare(a.value, b.value));
show("unsorted:", theArray);
show("lodashResult:", lodashResult);
show("localeCompareResult:", localeCompareResult);
show("collatorResult:", collatorResult);

function show(label, array) {
    console.log(label, "[");
    for (const element of array) {
        console.log(`    ${JSON.stringify(element)}`);
    }
    console.log("]");
}
.as-console-wrapper {
    max-height: 100% !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Stable vs Unstable Sort

When I first wrote this answer, there was a slight difference between _.orderBy and the native sort: _.orderBy, like _.sortBy, always does a stable sort, whereas at the time of the original answer JavaScript's native sort was not guaranteed to be stable. Since then, though, the JavaScript specification has been modified to require a stable sort (ES2019). So both _.orderBy/_.sortBy and native sort are stable now.

If "stable" vs. "unstable" sort aren't familiar terms: A "stable" sort is one where two elements that are considered equivalent for sorting purposes are guaranteed to remain in the same position relative to each other; in an "unstable" sort, their positions relative to to each other might be swapped (which is allowed because they're "equivalent" for sorting purposes). Consider this array:

const theArray = [
    {value: "x", id: 27},
    {value: "z", id: 14},
    {value: "x", id: 12},
];

If you do an unstable sort that sorts ascending on just value (disregarding id or any other properties the objects might have), there are two valid results:

// Valid result 1: id = 27 remained in front of id = 12
[
    {value: "x", id: 27},
    {value: "x", id: 12},
    {value: "z", id: 14},
]
// Valid result 2: id = 27 was moved after id = 12
[
    {value: "x", id: 12},
    {value: "x", id: 27},
    {value: "z", id: 14},
]

With a stable sort, though, only the first result is valid; the positions of equivalent elements relative to each other remains unchanged.

But again, that distinction no longer matters, since JavaScript's sort is stable now too.

like image 62
T.J. Crowder Avatar answered Oct 16 '22 17:10

T.J. Crowder