5

I'm trying to sort an array of objects by array of keys. I searched through similar questions but I didn't find anything that could help me

Example:

const data = [
 { label: 'String'},
 { label: 'Number'},
 { label: 'Boolean'},
 { label: 'Array'}
]

const order = [2, 3]

Expected result:

const data = [
 { label: 'Boolean'},
 { label: 'Array'},
 { label: 'String'},
 { label: 'Number'}
]

The problems that I'm facing are

  • order array can have or can not have the same length like data, the items key that not exists in order must go to the bottom
  • data objects has not an order key to help sort the element, the ordering will made by array object key position

What I tried but not working

data.sort((a, b) => {
 const aIndex = data.indexOf(a);
 const bIndex = data.indexOf(b);

 if(aIndex !== -1) return -1;
 if(bIndex !== -1) return 1;
 
 return order.indexOf(aIndex) - order.indexOf(bIndex);
})
3
  • 3
    How does [2,3] affect the output? The output you've shown seems to just be alphanumerically sorted. Commented Oct 3, 2022 at 8:59
  • 1
    In the order array I store the objects initial position that I want to sort and the new position that I want to put that object . The label string is only an exemple, it can be every text Commented Oct 3, 2022 at 9:05
  • Does it have to be sorted in place, or is returning a sorted copy acceptable? Commented Oct 3, 2022 at 9:06

4 Answers 4

4

You can just use a Set to map the data as you need, like this:

const data = [
 { label: 'String'},
 { label: 'Number'},
 { label: 'Boolean'},
 { label: 'Array'}
]

const order = [2, 3];

const out = [...new Set([...order.map(i => data[i]), ...data])];

console.log(out);

Sign up to request clarification or add additional context in comments.

2 Comments

My mistake was that I focused only on the sort method. Your solution seems very "clean". Thanks!
Sorting in such cases is unnecessary and not optimal, here you just need to transform some known data.
1

Instead of doing a sort, implement a straight-forward algorithm in two steps:

  1. Run through the order list and extract the corresponding items of data into a new list (the sorted list). Replace the extracted values with undefined in data.
  2. Run through data and append each item that is not undefined to the sorted list.

const data = [
 { label: 'Test 3'},
 { label: 'Test 4'},
 { label: 'Test 1'},
 { label: 'Test 2'}
];

const order = [2, 3, 1, 5];


// Create a working copy of `data` (to not modify `data`)
const work = [...data];

// The sorted list
const sorted = [];

// Step 1
// Run through `order` and move the items from `work` to `sorted` in the provided order
order.forEach((index) => {
  const item = data[index];
  // There is nothing to do if `index` is an invalid index (or `data` does not contain anything at that position)
  if (item !== undefined) {
    sorted.push(item);
    work[index] = undefined;
  }
});

// Step 2
// Move the remaining items (they are not mentioned in `order`)
work.forEach((item) => {
  // Ignore the items that were moved on the first step
  if (item !== undefined) {
    sorted.push(item);
  }
});

// Cleanup
delete work;

// Check the output visually
console.log(sorted);

Comments

1

Extract elements from order and then add those not in order:

const data = [
    { label: 'String'},
    { label: 'Number'},
    { label: 'Boolean'},
    { label: 'Array'}
]

const order = [2, 3]

result = [
    ...order.map(i => data[i]),
    ...data.filter((_, i) => !order.includes(i)),
]

console.log(result)

Comments

1

I really like @n--'s approach. However, it will run into difficulties if a certain value appears multiple times in the array. These problems can be overcome very easily when we build the Set with indexes instead of values of data:

const data = [
 { label: 'String'},
 { label: 'Number'},
 { label: 'Boolean'},
 { label: 'Array'},
 { label: 'String'}
];
const order = [2, 3];
const out = [...new Set([...order,...data.keys()])].map(i=>data[i]);

console.log(out);

2 Comments

Agree, this is an edge case which may exist in a buggy application. But usually, multiple references of the same object are undesired in a list, that could cause much more difficulties, so the Set behavior in such cases could easily be a useful feature.
Right, @n--! I overlooked the fact that my example would still work with your original Set approach. I used different objects. Still, in a wider sense, and especially when the array contains primitives instead of objects, the above mentioned problems might occur.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.