Sort an array to have specific items first in the array

Viewed 42731

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?

10 Answers

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

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

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

Related