javascriptarrayssorting

Javascript, partially reordering array


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.


Solution

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

    Update: even simpler version, which has about the same performance as Alexander's answer:

    const before = [23, 21, 14, 12, 10, 8, 15]
    const indexes = [2,1,4,0]
    
    const reorderByIndexes = (arr, order) => 
      [...order.map(e => arr[e]), ...arr.filter((e,i) => !order.includes(i))]
      
    const after = reorderByIndexes(before, indexes)
    
    console.log(after.join());

    const before = [23, 21, 14, 12, 10, 8, 15]
    const indexes = [2,1,4,0];
    
    const reorderByIndexes = (arr, order) => 
      [...order.map(e=>arr[e]), ...arr.filter((e,i)=>!order.includes(i))]
    
    const reorderByIndexes2 = (arr, order, r = order.map(e=>arr[e])) =>
      (r.push(...arr.filter((e,i)=>!order.includes(i))), r)
    
    const reorderByIndexes3 = (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 1
    reorderByIndexes(before, indexes);
    
    // @benchmark Andrew Parks 2
    reorderByIndexes2(before, indexes);
    
    // @benchmark Alexander
    reorderByIndexes3(before, indexes);
    
    /*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

    ` Chrome/123
    -------------------------------------------------------
    Andrew Parks 2  ■ 1.00x | x10000000 786 787 793 796 810
    Andrew Parks 1    1.04x | x10000000 818 841 852 858 863
    Alexander         1.09x | x10000000 856 857 865 872 875
    -------------------------------------------------------
    https://github.com/silentmantra/benchmark `