3

I want to sort an array of objects with labels, for example

var arrayToBeSorted = [{label: 'firstLabel', value: 123}, {label: 'secondLabel', value: 456}, {label: 'thirdLabel', value: 789}]

and an array of labels as a baseline, for example

var labels = ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel']

Now I want to sort my first array, so that the objects in it follow the order of labels in the second array labels.

I know basic sorting mechanism with custom comparators like

CustomComparator: function(a, b) {
  if (a[0].length > b[0].length) return -1;
  if (a[0].length < b[0].length) return 1;
  return 0;
}

but I have no idea on how to convert this.

In my research I found this solution on stackoverflow coded in ruby, but I don't know if there is a similar option in javascript.

I appreciate any help, invested quite some time on this.

Magelan
  • 189
  • 1
  • 13
  • 1
    https://stackoverflow.com/q/13304543/10221765 and convert to array of objects. – Jack Bashford Oct 29 '19 at 11:28
  • Nina pointed out a problem with one of the options in my answer which I've fixed just now. Pinging you to let you know because you've accepted my answer and so if you used that option... :-) – T.J. Crowder Oct 29 '19 at 12:07

4 Answers4

3

You need to provide priority to values as per second array. here we are building a Map from second array name as key and index as priority. so you can use Map and default value and sort

var arrayToBeSorted = [{label: 'firstLabel', value: 123}, {label: 'secondLabel', value: 456}, {label: 'thirdLabel', value: 789}]
var labels = ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel']

let mapper = new Map(labels.map((v, i) => [v, i + 1]))

let final = arrayToBeSorted.sort((a, b) => {
  return (mapper.get(a.label) || Infinity) - (mapper.get(b.label) || Infinity)
})

console.log(final)
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
2

There are a couple of approaches:

  1. Using indexOf to repeatedly search the labels array

  2. Using a map so looking up the index of a label is quicker

Here's an example using indexOf (in ES2015+):

arrayToBeSorted.sort((a, b) => labels.indexOf(a.label) - labels.indexOf(b.label));

Live Copy:

var arrayToBeSorted = [{label: 'firstLabel', value: 123}, {label: 'secondLabel', value: 456}, {label: 'thirdLabel', value: 789}];

var labels = ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel'];

arrayToBeSorted.sort((a, b) => labels.indexOf(a.label) - labels.indexOf(b.label));

console.log(arrayToBeSorted);

Note that indexOf will return -1 if the label doesn't exist in labels, which will make unknown labels appear at the beginning of the result. If you want them at the end instead, check for -1 and replace it with Infinity.

Here's an example using a map to speed up finding those indexes (in ES2015+):

const map = new Map(labels.map((label, index) => [label, index]));
arrayToBeSorted.sort((a, b) => {
    let aindex = map.get(a.label);
    if (aindex === null) {
        aindex = -1; // Or Infinity if you want them at the end
    }
    let bindex = map.get(b.label);
    if (bindex === null) {
        bindex = -1; // ""
    }
    return aindex - bindex;
});

Live Copy:

var arrayToBeSorted = [{label: 'firstLabel', value: 123}, {label: 'secondLabel', value: 456}, {label: 'thirdLabel', value: 789}];

var labels = ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel'];

const map = new Map(labels.map((label, index) => [label, index]));
arrayToBeSorted.sort((a, b) => {
    let aindex = map.get(a.label);
    if (aindex === null) {
        aindex = -1; // Or Infinity if you want them at the end
    }
    let bindex = map.get(b.label);
    if (bindex === null) {
        bindex = -1; // ""
    }
    return aindex - bindex;
});

console.log(arrayToBeSorted);

That's written for clarity and to avoid looking up the labels more than once in the callback. It can be more concise at the cost of a second label lookup in the map:

const map = new Map(labels.map((label, index) => [label, index]));
arrayToBeSorted.sort((a, b) => {
    const aindex = map.has(a.label) ? map.get(a.label) : -1; // Or Infinity if you want them at the end
    const bindex = map.has(b.label) ? map.get(b.label) : -1; // "
    return aindex - bindex;
});

Live Copy:

var arrayToBeSorted = [{label: 'firstLabel', value: 123}, {label: 'secondLabel', value: 456}, {label: 'thirdLabel', value: 789}];

var labels = ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel'];

const map = new Map(labels.map((label, index) => [label, index]));
arrayToBeSorted.sort((a, b) => {
    const aindex = map.has(a.label) ? map.get(a.label) : -1; // Or Infinity if you want them at the end
    const bindex = map.has(b.label) ? map.get(b.label) : -1; // "
    return aindex - bindex;
});

console.log(arrayToBeSorted);

It can even be:

const map = new Map(labels.map((label, index) => [label, index]));
arrayToBeSorted.sort((a, b) =>
    (map.has(a.label) ? map.get(a.label) : -1) - (map.has(b.label) ? map.get(b.label) : -1)
);

...but for me that's making life too difficult when debugging, etc.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • @NinaScholz - Gah, -1 in the second approach is a Bad Idea,™ thanks. In the first it really doesn't matter much what value you use. I did mean to include a note saying something about the default. I'll fix the -1 problem and add that note, thanks. – T.J. Crowder Oct 29 '19 at 12:00
1

You can create a custom order and order by it:

var arrayToBeSorted = [
    {label: 'firstLabel', value: 123}, 
    {label: 'secondLabel', value: 456}, 
    {label: 'thirdLabel', value: 789}
];

let order = { secondLabel: 1, thirdLabel: 2, fourthLabel: 3, firstLabel: 4 };

arrayToBeSorted.sort((a, b) => {
  return order[a.label] - order[b.label];
});

console.log(arrayToBeSorted);
StepUp
  • 36,391
  • 15
  • 88
  • 148
0

Straight-forward using sort-array.

const sortArray = require('sort-array')

const arrayToBeSorted = [
  {label: 'firstLabel', value: 123},
  {label: 'secondLabel', value: 456},
  {label: 'thirdLabel', value: 789}
]

sortArray(arrayToBeSorted, {
  by: 'label',
  order: 'labelOrder',
  customOrders: {
    labelOrder: ['secondLabel', 'thirdLabel', 'fourthLabel', 'firstLabel']
  }
})

console.log(arrayToBeSorted)

Prints this output:

[
  { label: 'secondLabel', value: 456 },
  { label: 'thirdLabel', value: 789 },
  { label: 'firstLabel', value: 123 }
]
Lloyd
  • 8,204
  • 2
  • 38
  • 53