简体   繁体   中英

How to sort array of objects and subset of array

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']);

more info on orderBy

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM