'Handle null with localecompare typescript

I have the following sort:

let sortedCon = this.con.sort((a, b) => a.lastname.localeCompare(b.lastname));

I am trying to get it to handle nulls and maintain the one line simplicity.

I have tried this but not working:

let sortedCon = this.con.sort((a, b) => a ? a.lastname.localeCompare(b ? b.lastname : '') : 0);

I know I am close but cant figure it out. Any help would be appreciated.



Solution 1:[1]

Without a minimum reproducible example including the type definitions for this.con as well as the particular sort order you'd like to see (if an element of this.con is not defined, should it sort in the same place as if it were defined with an empty lastname? or somewhere else?), it's hard to be sure, but assuming it's something like:

declare const con: Array<{
    lastname?: string | null
} | null | undefined>;

then you can use the optional chaining and nullish coalescing operators introduced in TypeScript 3.7 to get the following one-liner:

con.sort((a, b) => (a?.lastname ?? "").localeCompare(b?.lastname ?? ""));

The optional chaining in a?.lastname is similar to (a != null ? a.lastname : undefined), and the nullish coalescing expr ?? "" is similar to (expr != null ? expr : ""). So that together produces something similar to

con.sort((a, b) => ((a || {}).lastname || "").localeCompare((b || {}).lastname || ""));

which is uglier but should work in TS3.6 and below. Both of these make the elements null, {lastname: undefined}, {lastname: null}, and {lastname: ""} all interchangeable in terms of the sort order. If you want to see those sorted in some particular order with respect to each other or with respect to elements with non-empty lastname properties, then you should specify that in the question.


Let's see how it acts:

const con: Array<{ lastname?: string | null } | null | undefined> = [
    { lastname: "" },
    { lastname: "B" },
    { lastname: null },
    null,
    { lastname: "A" },
    {},
    { lastname: "C" }
]    
console.log(JSON.stringify(con));
// [{"lastname":""},{"lastname":"B"},{"lastname":null},null,{"lastname":"A"},{},{"lastname":"C"}]

Sorting with the TS3.7 code:

const newCon = con.slice();
newCon.sort((a, b) => (a?.lastname ?? "").localeCompare(b?.lastname ?? ""));
console.log(JSON.stringify(newCon));
// [{"lastname":""},{"lastname":null},null,{},{"lastname":"A"},{"lastname":"B"},{"lastname":"C"}]

and with the TS3.6- code:

const newCon2 = con.slice();
newCon2.sort((a, b) => ((a || {}).lastname || "").localeCompare((b || {}).lastname || ""));
console.log(JSON.stringify(newCon2));
// [{"lastname":""},{"lastname":null},null,{},{"lastname":"A"},{"lastname":"B"},{"lastname":"C"}]

both yields the same ordering, where the "empty" elements are moved to the front and in the original order from the array (you can't count on Array.prototype.sort being stable but it usually is), and the rest of the elements are sorted in order of the lastname property.

Playground link to code

Anyway, hope that helps; good luck!

Solution 2:[2]

Have you tried something like this?

let sortedCon = this.con.sort((a, b) => a && a.lastname && a.lastname.localeCompare(b && b.lastname : '') : 0);

Solution 3:[3]

It's easy, you need to evaluate the lastname field, if the lastname is null change it to an empty string ---> ""

this.con.sort((a:any, b:any) => (a.lastName || "").localeCompare(b.lastName || "")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3