Schema
var my_array = [
{
"title": "a",
"pinned": {
"status": "true",
"order": "1"
}
},
{
"title": "d",
"pinned": {
"status": "false",
"order": "n/a"
}
},
{
"title": "c",
"pinned": {
"status": "true",
"order": "0"
}
},
{
"title": "b",
"pinned": {
"status": "false",
"order": "n/a"
}
}
];
Desired Behaviour
Sort objects by title
alphabetically,
unless they have a pinned
status of true
,
in which case move this "subset" of items to the beginning of the array,
sorted by their pinned.order
value.
An example scenario would be a forum which had posts sorted by date, but also had sticky posts at the top, which were sorted by a defined order.
The original schema would, therefore, be displayed as:
[
{// i am at the top, because i have a pinned status of true and order of 0
"title": "c",
"pinned": {
"status": "true",
"order": "0"
}
},
{// i am second from the top, because i have a pinned status of true and order of 1
"title": "a",
"pinned": {
"status": "true",
"order": "1"
}
},
{// i follow in alphabetical order
"title": "b",
"pinned": {
"status": "false",
"order": "n/a"
}
},
{// i follow in alphabetical order
"title": "d",
"pinned": {
"status": "false",
"order": "n/a"
}
}
]
What I've Tried
my_array.sort(function(a, b) {
return a.pinned.order.localeCompare(b.pinned.order) || a.title.localeCompare(b.title);
});
based on this answer:
https://stackoverflow.com/a/45741804
I also tried...
I thought about creating two separate arrays based on the value of pinned.status
, sorting them separately, and then recombining them (as shown below), but I'm wondering if there is something more elegant?
var my_array = [
{
"title": "a",
"pinned": {
"status": "true",
"order": "1"
}
},
{
"title": "d",
"pinned": {
"status": "false",
"order": "n/a"
}
},
{
"title": "c",
"pinned": {
"status": "true",
"order": "0"
}
},
{
"title": "b",
"pinned": {
"status": "false",
"order": "n/a"
}
}
];
var my_subset = [];
for (var i = 0; i < my_array.length; i++) {
if (my_array[i].pinned.status === "true") {
// add to subset
my_subset.push(my_array[i]);
// remove from original array
my_array.splice(i, 1);
}
}
// sort "pruned" original array alphabetically
my_array.sort(function(a, b) {
return a.title.localeCompare(b.title);
});
// sort subset array by pinned.order
my_subset.sort(function(a, b) {
return a.pinned.order.localeCompare(b.pinned.order, undefined, { numeric: true });
});
// prepend subset to original array
var new_array = my_subset.concat(my_array);
// array is sorted as desired
console.log(new_array);
First fix the data by making the numeric string a number and the boolean string a boolean:
for (const item of my_array) {
item.pinned.status = JSON.parse(item.pinned.status);
item.pinned.order = Number(item.pinned.order);
}
Now you won't have to compare them as strings. Otherwise your approach is basically fine, you just forgot the most significant indicator whether an item should go to the top or not: its pinned.status
. Compare by that first, so that any pinned item comes before any not pinned item.
my_array.sort(function(a, b) {
return -(a.pinned.status - b.pinned.status) // reverse: true before false
|| (a.pinned.status // equal to b.pinned.status
? a.pinned.order - b.pinned.order
: a.title.localeCompare(b.title));
});
var my_array = [{ "title": "a", "pinned": { "status": true, "order": 1 } }, { "title": "d", "pinned": { "status": false, "order": 0 } }, { "title": "c", "pinned": { "status": true, "order": 0 } }, { "title": "b", "pinned": { "status": false, "order": 0 } } ]; my_array.sort(function(a, b) { return -(a.pinned.status - b.pinned.status) // reverse: true before false || (a.pinned.status // equal to b.pinned.status ? a.pinned.order - b.pinned.order : a.title.localeCompare(b.title)); }); console.log(my_array);
You can also do
my_array.sort(function(a, b) {
return -(a.pinned.status - b.pinned.status) // reverse: true before false
|| a.pinned.order - b.pinned.order
|| a.title.localeCompare(b.title);
});
as not-pinned items have the same order ( NaN
) but the former is more explicit.
Just try this:
my_array.sort(function(a, b) {
return a.title.localeCompare(b.title);
}).sort(function(a, b) {
return a.pinned.order.localeCompare(b.pinned.order)
});
If you can use Lodash (utility library for Javascript) you can use orderBy or sortBy:
use Lodash in project:
<script src="lodash.js"></script>
use orderBy to sort:
_.orderBy(my_array, [function(e) { return e.pinned.status}, 'title'], ['asc', 'asc']);
there you go:
var my_array = [ { "title": "a", "pinned": { "status": "true", "order": "1" } }, { "title": "d", "pinned": { "status": "false", "order": "n/a" } }, { "title": "c", "pinned": { "status": "true", "order": "0" } }, { "title": "b", "pinned": { "status": "false", "order": "n/a" } } ]; var trueArr = []; var falseArr = []; var titleArr = []; var tempArr = [] for(var obj of my_array){ if(obj.pinned.status == "true"){ trueArr.push(obj); }else{ falseArr.push(obj); } } function sortArr(arr){ titleArr = []; tempArr = []; for(var obj of arr){ titleArr.push(obj.title); } titleArr = titleArr.sort(); for(var counter = 0; counter < arr.length; counter++){ tempArr.push(null); } for(var obj of arr){ tempArr[titleArr.indexOf(obj.title)] = obj; } for(counter = 0; counter < tempArr.length; counter++){ arr[counter] = tempArr[counter]; } } function addTrueFalseArr(arr){ for (var obj of arr){ my_array.push(obj) } } sortArr(trueArr); my_array = []; addTrueFalseArr(trueArr); sortArr(falseArr); addTrueFalseArr(falseArr);
extracts all objects where pinned = true
and puts them in an array. Then it does same for objects where pinned = false
. Then it sorts both arrays according to their titles (alphabetically), then sets original array to an empty array --> []
, then appends the true
items to the original array and, finally, appends the false
items.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.