I was wondering if there was a better or smarter way of sorting objects in JavaScript when sorting for multiple items?
For example, if I have the following JSON:
json = [
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "truck", "year": 2021, "origin": "USA" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "truck", "year": 2020, "origin": "CA" },
{ "type": "bike", "year": 2000, "origin": "AUS" }
]
If I wanted to sort it by type
then year
how would I do that in the same .sort
function?
I've currently got it doing one, then the other - but when I try to combine them it doesn't seem to output correctly:
// one after other
json.sort( ( a, b ) => {
a = a.type.toLowerCase();
b = b.type.toLowerCase();
return a < b ? -1 : a > b ? 1 : 0;
})
.sort( ( a, b ) => {
a = a.year;
b = b.year;
return a < b ? -1 : a > b ? 1 : 0;
});
Returning:
[
{ "type": "bike", "year": 2000, "origin": "AUS" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "truck", "year": 2020, "origin": "USA" },
{ "type": "truck", "year": 2021, "origin": "CA" }
]
When it should return (all the types together, then by year):
[
{ "type": "bike", "year": 2000, "origin": "AUS" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "truck", "year": 2020, "origin": "USA" },
{ "type": "truck", "year": 2021, "origin": "CA" }
]
CodePudding user response:
In the sort
callback, compare the items' type
property. If one is lexographically greater/smaller, sort it before/after the other item accordingly.
Otherwise, you can return the result after subtracting the two items' year
property:
const arr = [
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "truck", "year": 2021, "origin": "USA" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "truck", "year": 2020, "origin": "CA" },
{ "type": "bike", "year": 2000, "origin": "AUS" }
]
const sorted = arr.sort((a,b) => a.type.localeCompare(b.type) || a.year - b.year)
console.log(sorted)
Credit to DraganS
If you don't want the comparison to be case-sensitive, you can set localeCompare
's sensitivity
option to accent
:
const arr = [
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "truck", "year": 2021, "origin": "USA" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "truck", "year": 2020, "origin": "CA" },
{ "type": "bike", "year": 2000, "origin": "AUS" }
]
const sorted = arr.sort((a,b) => a.type.localeCompare(b.type, undefined, {sensitivity:'accent'}) || a.year - b.year)
console.log(sorted)
CodePudding user response:
You can do this with one sort
by first checking if the types are equal. If so, sort on the date
. If not, sort on the type.
const json = [
{ "type": "car", "year": 2007, "origin": "AUS" },
{ "type": "truck", "year": 2021, "origin": "USA" },
{ "type": "car", "year": 2004, "origin": "CA" },
{ "type": "bike", "year": 2016, "origin": "UK" },
{ "type": "truck", "year": 2020, "origin": "CA" },
{ "type": "bike", "year": 2000, "origin": "AUS" }
]
// one after other
json.sort( ( a, b ) => {
a = a.type.toLowerCase();
b = b.type.toLowerCase();
if (a === b) {
return a.year - b.year;
}
return a < b ? -1 : 1;
});
console.log(json);
CodePudding user response:
If you do one sort followed by a different sort, the final sort overrides any previous sort.
Combine them into the same lambda
json.sort( ( a, b ) => {
aType = a.type.toLowerCase();
bType = b.type.toLowerCase();
aYear = a.year;
bYear = b.year;
return aType < bType
? -1
: aType > bType
? 1
: aYear < bYear
? -1
: aYear > bYear
? 1
: 0;
})
Though this has gotten pretty unreadable. You can have multiple comparison functions:
let compareByType = (a, b) => {
aType = a.type.toLowerCase();
bType = b.type.toLowerCase();
return (aType<bType) ? -1 : (aType>bType ? 1 : 0);
}
let compareByYear = (a,b) => {
return a.year - b.year;
}
json.sort(
(a, b) => {
let s = compareByType(a, b);
if(s !== 0) {
return s;
} else {
return compareByYear(a, b);
}
}
);