首页 > 解决方案 > 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);

标签: javascriptsorting

解决方案


首先通过将数字字符串设为数字并将布尔字符串设为布尔值来修复数据:

for (const item of my_array) {
    item.pinned.status = JSON.parse(item.pinned.status);
    item.pinned.order = Number(item.pinned.order);
}

现在您不必将它们作为字符串进行比较。否则你的方法基本上没问题,你只是忘记了一个项目是否应该放在顶部的最重要的指标:它的pinned.status. 首先进行比较,以便任何已固定的项目出现在任何未固定的项目之前。

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

你也可以做

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);
});

因为未固定的项目具有相同的顺序 ( NaN) 但前者更明确。


推荐阅读