There's an object:
let obj = [
{'id': 4, 'surname': 'white', 'is_curator': true},
{'id': 7, 'surname': 'goodman', 'is_curator': false},
{'id': 1, 'surname': 'black', 'is_curator': true},
{'id': 9, 'surname': 'babbs'},
{'id': 3, 'surname': 'smith', 'is_curator': true}
]
(I skipped is_curator field in one of the arrays intentionally)
and an array containing some of the object's ids, e.g.
let idsArr = [3, 1, 7]
I want to sort my object primarily to have all arrays which ids are included in the idsArr at the top, secondarily arrays with is_curator = true are sorted true to false, and then these arrays must be sorted by surname A to z, so the object looks like:
obj = [
{'id': 1, 'surname': 'black', 'is_curator': true},
{'id': 3, 'surname': 'smith', 'is_curator': true},
{'id': 7, 'surname': 'goodman', 'is_curator': false},
{'id': 4, 'surname': 'white', 'is_curator': true},
{'id': 9, 'surname': 'babbs'},
]
I tried to use nested conditions in the array.sort function, tried to make a custom compare function but haven't got the required result.
I tried:
data.sort(function (a, b) {
return idsArr.includes(b.id) - idsArr.includes(a.id)
|| b.is_curator - a.is_curator
|| b.surname.localeCompare(a.surname)
});
and something like
if (idsArr.includes(a.id) && !idsArr.includes(b.id) {
if (a.is_curator && !b.is_curator) {
return -1;
} else if (!a.is_curator && b.is_curator) {
return 1;
}
} else if (!idsArr.includes(a.id) && idsArr.includes(b.id)) {
return 1;
}
CodePudding user response:
Break the sort down into logical comparisons to short-circuit as early as possible.
- Check for
null
- Check if the ID is present in the priority array
- Find the logical difference between the
is_curator
field- Use nullish-coalescing (
??
) to default tofalse
- Use nullish-coalescing (
- Compare by surname
const people = [
{ id: 4 , surname: 'white' , is_curator: true },
{ id: 7 , surname: 'goodman' , is_curator: false },
{ id: 1 , surname: 'black' , is_curator: true },
{ id: 9 , surname: 'babbs' },
{ id: 3 , surname: 'smith' , is_curator: true }
];
const idsArr = [3, 1, 7];
const customSort = (items, idPriority) => {
const ids = new Set(idPriority);
return items.sort((a, b) => {
if (!b) return -1;
if (!a) return 1;
if (ids.has(a.id) && !ids.has(b.id)) return -1;
if (ids.has(b.id) && !ids.has(a.id)) return 1;
let diff = (b.is_curator ?? false) - (a.is_curator ?? false);
if (diff !== 0) return diff;
return a.surname.localeCompare(b.surname);
});
};
const sorted = customSort(people, idsArr);
console.log(sorted);
.as-console-wrapper { top: 0; max-height: 100% !important; }
CodePudding user response:
I suppose your intended object is this (correcting invalid syntax):
[
{'id': 4, 'surname': 'white', 'is_curator': true},
{'id': 7, 'surname': 'goodman', 'is_curator': false},
{'id': 1, 'surname': 'black', 'is_curator': true},
{'id': 9, 'surname': 'babbs'},
{'id': 3, 'surname': 'smith', 'is_curator': true}
]
Your first attempt is almost right, but:
you need to cope with the non-existence of the
is_curator
property. For that, it is probably easiest to negate that property value -- which you did in the second attempt, but there you abandoned the simple subtraction pattern and will sometimes return undefined.To sort by ascending
surname
, you should have the negation of what you tried. So swap the position ofa
andb
in that expression:a.surname.localeCompare(b.surname)
.
Code:
obj.sort((a, b) => idsArr.includes(b.id) - idsArr.includes(a.id)
|| !a.is_curator - !b.is_curator
|| a.surname.localeCompare(b.surname));