skip to Main Content

What is the best way to partially re-order an array?

I have created a very basic example;
Basically I want to reorder the array (before) using the (indexes) stored in the array.

Using the code I have outputs: 14,21,10,13.
However I would like to then keep the remainder of the array that isn’t included added to the new after array in the order they originally appear.

This would leave the after array populated as the following: 14,21,10,13 23,8,15

const before = [23, 21, 14, 12, 10, 8, 15]
const indexes = [2,1,4,0];

const reorderByIndexes = (arr, order) => order.map((index) => arr[index]);

const after = reorderByIndexes(before, indexes);

console.log(after.join());
// 14,21,10,13 23,8,15

I know on a simplistic example like this I could use a loop to iterate over them but the final version is set to be a lot larger.

3

Answers


  1. I’m assuming there is a typo in your question, and the expected output is 14,21,10,23,12,8,15

    Order the indices of arr by either the position of the index in order, or otherwise by the index itself + order.length.

    We add order.length so that something at index 1 in order appears before something not in order that is at index 0 in arr.

    Then, map the indices to the elements of arr

    const before = [23, 21, 14, 12, 10, 8, 15]
    const indexes = [2,1,4,0];
    
    const reorderByIndexes = (arr, order) => {
      const f = i => {
        let r = order.indexOf(i)
        return r!==-1 ? r : i + order.length
      }
      return arr.map((_,i) => i).sort((i,j) => f(i) - f(j)).map(i => arr[i])
    }
    
    const after = reorderByIndexes(before, indexes)
    
    console.log(after.join());
    Login or Signup to reply.
  2. To make it efficient, just pre-allocate the array and fill its 2 halves:

    const before = [23, 21, 14, 12, 10, 8, 15]
    const indexes = [2,1,4,0];
    
    const reorderByIndexes = (arr, order) => {
      const r = Array(arr.length);
      const set = new Set(indexes);
      let j = order.length;
      arr.forEach((n, i) => {
        if(i < order.length) r[i] = arr[order[i]];
        if(!set.has(i)) r[j++] = n;
      });
      return r;
    }
    
    const after = reorderByIndexes(before, indexes);
    
    console.log(...after);

    And a benchmark:

    ` Chrome/123
    -----------------------------------------------------
    Alexander     ■ 1.00x | x10000000 710 728 731 748 757
    Andrew Parks    3.59x |  x1000000 255 257 258 262 297
    -----------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const before = [23, 21, 14, 12, 10, 8, 15]
    const indexes = [2,1,4,0];
    
    const reorderByIndexes = (arr, order) => {
      const f = i => {
        let r = order.indexOf(i)
        return r!==-1 ? r : i + order.length
      }
      return arr.map((_,i) => i).sort((i,j) => f(i) - f(j)).map(i => arr[i])
    }
    
    const reorderByIndexes2 = (arr, order) => {
      const r = Array(arr.length);
      const set = new Set(indexes);
      let j = order.length;
      arr.forEach((n, i) => {
        if(i < order.length) r[i] = arr[order[i]];
        if(!set.has(i)) r[j++] = n;
      });
      return r;
    }
    
    // @benchmark Andrew Parks
    reorderByIndexes(before, indexes);
    
    // @benchmark Alexander
    reorderByIndexes2(before, indexes);
    
    /*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
    Login or Signup to reply.
  3. You could sort the indices and build an object for getting new indices.

    Finally map the values by source index or the actual index.

    const
        before = [23, 21, 14, 12, 10, 8, 15],
        indexes = [2, 1, 4, 0],
        reorderByIndexes = (values, order) => {
            const
                sorted = order.toSorted((a, b) => a - b),
                source = Object.fromEntries(sorted.map((i, j) => [i, order[j]]))
            
            return values.map((_, i, a) => a[source[i] ?? i]);
        },
        after = reorderByIndexes(before, indexes);
    
    console.log(...after);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search