Florin D
Florin D

Reputation: 59

Sort array of objects by array of indexes

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

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

Upvotes: 5

Views: 194

Answers (4)

Carsten Massmann
Carsten Massmann

Reputation: 28196

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

Upvotes: 1

gog
gog

Reputation: 11347

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)

Upvotes: 1

n--
n--

Reputation: 3856

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

Upvotes: 4

axiac
axiac

Reputation: 72226

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

Upvotes: 1

Related Questions