简体   繁体   中英

How to sort an array of objects with labels according to other array of labels?

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.

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)

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.

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

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

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