开发者

Sort an array to have specific items first in the array

I've got an array as such:

[{flag: true, other: 1},
 {flag: true, other: 2},
 {flag: false, other: 3},
 {flag: true, other: 4},
 {flag: true, other: 5},
 开发者_运维问答{flag: true, other: 6},
 {flag: false, other: 7}]

I want to have this:

[{flag: false, other: 3},
 {flag: false, other: 7},
 {flag: true, other: 1},
 {flag: true, other: 2},
 {flag: true, other: 4},
 {flag: true, other: 5},
 {flag: true, other: 6}]

Basically I want that if array[2].flag === false (or any other value I choose) the matching element gets placed first in the array, but after previous matching elements. The elements that do not match, remain in the same order they were in originally.

Order of appearance is important.

How to do this best in JavaScript?


The Spread syntax introduced with ECMAScript6 (e.g., [...object]) makes this relatively straightforward using an Array's reduce method:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

const sortedArr = arr.reduce((acc, element) => {
  if (!element.flag) {
    return [element, ...acc];
  }
  return [...acc, element];
}, []);

I found this example of extended parameter handling really helpful.


A simpler and more elegant way to do is by building a new array by filtering the old one twice: once filtered by flag: true and once by flag: false. All together it would look like:

// THE INITIAL ARRAY:
const originalArray = [
    {flag: true, other: 1},
    {flag: true, other: 2},
    {flag: false, other: 3},
    {flag: true, other: 4},
    {flag: true, other: 5},
    {flag: true, other: 6},
    {flag: false, other: 7}
];

// THE SORTED ARRAY:
const sortedArray = [
    ...originalArray.filter(({flag}) => flag),
    ...originalArray.filter(({flag}) => !flag)
];

In my opinion it is way easier for a human to read than using comparer or reducer functions, and (based on measurements) it is also a pretty well-performing solution.


A performance comparison:

I saw that the most upvoted answer using arr.reduce() performs around 20x slower than this solution. I used ES6 Console for comparison, feel free to test it yourself too!

Measuring the array.reduce() way:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

// Lets multiple the array items 11 times to increase the looping process:
new Array(11).fill().forEach(x => arr.push(...arr));

console.time();

const reducedArr = arr.reduce((acc, element) => {
  if (element.flag === false) {
    return [element, ...acc];
  }
  return [...acc, element];
}, []);

console.timeEnd(); // RESULTS: between 285-350ms

Measuring the array.filter() way:

const arr = [
  { flag: true, other: 1 },
  { flag: true, other: 2 },
  { flag: false, other: 3 },
  { flag: true, other: 4 },
  { flag: true, other: 5 },
  { flag: true, other: 6 },
  { flag: false, other: 7 }
];

// Lets multiple the array items 11 times to increase the looping process:
new Array(11).fill().forEach(x => arr.push(...arr));

console.time();

const rebuiltArray = [
  ...arr.filter(x => x.flag),
  ...arr.filter(x => !x.flag)
];

console.timeEnd(); // RESULTS: between 6-20ms


Write a custom sort function that uses the flag to increase the sort priority:

array.sort(function(a,b) {
  if (!a['flag'] && b['flag'])
    return 1;
  if (a['flag'] && !b['flag'])
    return -1;
  return a['other'] - b['other']
});

Basically, I'm assuming that an entry in the list with the flag set takes priority over an item without the flag. So if a doesn't have the flag set and b does, return 1 (select b). If a does and b doesn't return -1 (select a).

In the case where both have the flag set or both don't, cmp as normal.


This isn't actually sorting. You can just loop through the array twice and build a new array:

var result = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i].flag === false) {
    result.push(arr[i]);
  }
}
for (var i = 0; i < arr.length; i++) {
  if (!arr[i].flag === false) {
    result.push(arr[i]);
  }
}

You can also do it with two result arrays and one loop, and concatenate the results:

var result1 = [], result2 = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i].flag === false) {
    result1.push(arr[i]);
  } else {
    result2.push(arr[i]);
  }
}
var result = result1.concat(result2);


I think this is a little simple. Because javascript treats true as 1 and false as 0 you can use them to create a comparator like this,

var comp = function(a,b){
    return a.flag*1 - b.flag*1;
}

and then you can use this comparator to sort the array

var arr = [{flag: true, other: 1},
         {flag: true, other: 2},
         {flag: false, other: 3},
         {flag: true, other: 4},
         {flag: true, other: 5},
         {flag: true, other: 6},
         {flag: false, other: 7}];
arr.sort(comp);


You could sort with the deltas of the values.

  1. Sort by flag. The boolean values are converted to 1, if true or 0, if false.
  2. Sort by other.

var array = [{flag: true, other: 1 }, { flag: true, other: 2 }, { flag: false, other: 3 }, { flag: true, other: 4 }, { flag: true, other: 5 }, { flag: true, other: 6 }, { flag: false, other: 7 }];

array.sort((a, b) =>
    a.flag - b.flag ||
    a.other - b.other
);

console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }


The sort method can take an optional sortFunction as a parameter that you can use to define the ordering of elements after the sort

http://www.w3schools.com/jsref/jsref_sort.asp


Since you want to select certain array items but keep them in their original order, you want the filter() method. This function is an extension to the standard, so you may need to use the shim provided at that page depending on what browsers you’re supporting.

(When I first read your question I thought that you wanted to sort based on the flag, and then based on value; for that you could use the built-in sort() method.)

var input = [{flag: true, other: 1},
             {flag: true, other: 2},
             {flag: false, other: 3},
             {flag: true, other: 4},
             {flag: true, other: 5},
             {flag: true, other: 6},
             {flag: false, other: 7}]
var false_items = input.filter(
        function(val, idx, arr) {
            return (val["flag"] == false);
        });
var true_items = input.filter(
        function(val, idx, arr) {
            return (val["flag"] == true);
        });

var sorted = false_items.concat(true_items);


One solution can be to sort the array on the basis of flag and then by the other

var a = [{flag: true, other: 1},
 {flag: true, other: 2},
 {flag: false, other: 3},
 {flag: true, other: 4},
 {flag: true, other: 5},
 {flag: true, other: 6},
 {flag: false, other: 7}];

a.sort((a, b)=> a.flag - b.flag).sort((a,b) => a.other - b.other);


arr = arr.sort(function(a, b) { // sort the array using a custom function,
                                // which is called a number of times to
                                // determine the new order. a and b are two
                                // elements to compare. JavaScript uses the
                                // return value to determine the new order:
                                // 1  - a should come later than b
                                // -1 - a should come earlier than b
                                // 0  - they should stay in their current order
    return a.flag === true && b.flag === false
            ? 1 // if a is true and b is false a should come later on
            : a.flag === false && b.flag === true
               ? -1 // if a is false and b is true a should come earlier
               : a.other > b.other
                  ? 1 // if a.other > b.other, a must come later on
                  : a.other < b.other
                    ? -1 // if a.other < b.other, a must come earlier
                    : 0; // otherwise they are equal, so they have no order difference
});
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜