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_curatorfield- 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_curatorproperty. 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 ofaandbin 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));
