Reputation: 492
var array = [
{
"checked": true,
"name":"Name",
"type": {
"id": 1,
"tag": "tag"
}
},
{
"checked": true,
"name":"Name",
"type": {
"id": 3,
"tag": "tag"
}
},
{
"checked": false,
"name":"Name",
"type": {
"id": 2,
"tag": "tag"
}
},
];
I want to sort the array by checked and type.id. Im using the following sorting code but it seems to have trouble with "type.id" since my list is not grouped by those if checked are.
sortByPriority(array, ['checked', 'type.id']);
sortByPriority(data, priorities) {
if (priorities.length == 0 || data.length == 0) {
return data;
}
const nextPriority = priorities[0];
const remainingPriorities = priorities.slice(1);
const matched = data.filter(item => item.hasOwnProperty(nextPriority));
const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));
return this.sortByPriority(matched, remainingPriorities)
.sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
.concat(this.sortByPriority(remainingData, remainingPriorities));
}
Any ideas on how to sort on the type object as well?
(I haven't been able to find another question with an answer with a generic sorter able to sort based on objects in the array)
Upvotes: 1
Views: 225
Reputation: 386520
You could use a nested approach for the nested properties and take an array of function for comparing the values.
function sortByPriority(array, keys, fn) {
function getValue(o, k) {
return k.split('.').reduce((p, l) => (p || {})[l], o);
}
return array.sort((a, b) => {
var d;
keys.some((k, i) => d = fn[i](getValue(a, k), getValue(b, k)));
return d;
});
}
var array = [{ checked: false, name: "Name", type: { id: 2, tag: "tag" } }, { checked: true, name: "Name", type: { id: 3, tag: "tag" } }, { checked: true, name: "Name", type: { id: 1, tag: "tag" } }];
sortByPriority(array, ['checked', 'type.id'], [(a, b) => b - a, (a, b) => a - b]);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 3373
Here is a generic solution using composed sorts, in which you can combine an arbitrary number of sorting conditions into one large sort function:
function to_tuples(array_of_elements) {
return array_of_elements.map((element, index) => [element, index]);
}
function to_elements(array_of_tuples) {
return array_of_tuples.map((element) => element[0]);
}
function compose_sorts(sort_functions) {
const default_sort = (l, r) => l[1] < r[1];
return sort_functions.reduceRight((master_sort, sort_function) => {
const mixin = (next_sort, left, right) => {
const result = sort_function(left, right);
return result === 0 ? next_sort(left, right) : result;
}
return mixin.bind(null, master_sort);
}, default_sort);
}
function sort_checked_to_top(left_tuple, right_tuple) {
const left = left_tuple[0];
const right = right_tuple[0];
if (left.checked !== right.checked) {
return left.checked ? -1 : 1;
}
return 0;
}
function sort_lower_id_to_top(left_tuple, right_tuple) {
const left = left_tuple[0];
const right = right_tuple[0];
if (left.type.id !== right.type.id) {
return left.type.id > right.type.id ? 1 : -1;
}
return 0;
}
const master_sort = compose_sorts([
sort_checked_to_top,
sort_lower_id_to_top,
]);
var array = [
{
"checked": true,
"name":"Name",
"type": {
"id": 1,
"tag": "tag"
}
},
{
"checked": false,
"name":"Name",
"type": {
"id": 25,
"tag": "tag"
}
},
{
"checked": true,
"name":"Name",
"type": {
"id": 3,
"tag": "tag"
}
},
{
"checked": false,
"name":"Name",
"type": {
"id": 2,
"tag": "tag"
}
},
];
const result = to_elements(to_tuples(array).sort(master_sort));
console.log(result);
Upvotes: 0
Reputation: 3166
Overload the sort
method on the array and then you can just switch the <
and >
based on your sort conditions. Note I removed the return 0
if they are equal on the first sort in order to move onto the second sort if the checked
values are equal.
var array = [
{
"checked": true,
"name":"Name",
"type": {
"id": 1,
"tag": "tag"
}
},
{
"checked": true,
"name":"Name",
"type": {
"id": 3,
"tag": "tag"
}
},
{
"checked": false,
"name":"Name",
"type": {
"id": 2,
"tag": "tag"
}
},
];
function customSort(ob1, ob2) {
if (ob1.checked > ob2.checked) {
return 1;
} else if (ob1.checked < ob2.checked) {
return -1;
}
// Else sort on the second item
if (ob1.type.id < ob2.type.id) {
return -1;
} else if (ob1.type.id > ob2.type.id) {
return 1
} else {
return 0;
}
}
console.log(array);
console.log(array.sort(customSort));
Upvotes: 1