skip to Main Content

I have a chain of methods that treat an array. Then I was trying to do a simple thing: add an element at the start of the array in a method chain. Initially I tried:

console.log(
    [1, 2, 3]
        .map(a=>a+1)
        .splice(0, 0, 7)
        .map(a=>a*10)
)

I used splice, because unshift returns the number of elements in the array. So hoped the result would be [70, 20, 30, 40], but the result was [] as splice returns the elements deleted in the array.

I give it a thought and couldn’t find any other way to do this. Not even with .apply.

That’s a simple thing I can easily solve by breaking the chain, but I thought it might be an oportunity to learn a bit (and don’t like to give up that easily :D), so searching a bit I found only one way to chain this:

console.log(
    [2, 3, 4]
        .map(a=>a+1)
        .concat(7)
        .map((e, i, a) => i == 0 ? a[a.length - 1] : a[i-1])
        .map(a=>a*10)
)

I mean… really ? there should be a better way to chain a simple insert at first position. Anyone knows a better way ?

3

Answers


  1. Chosen as BEST ANSWER

    Based on the answers and comments, as I think that toSpliced() method is too new. I think I decided myself by:

    console.log(
        [2, 3, 4]
            .map(a=>a+1)
            .reduce((a, v)=>(a.push(v), a), [7])
            .map(a=>a*10)
    )

    There is also the slow but more readable option below.

    console.log(
        [2, 3, 4]
            .map(a=>a+1)
            .reduce((a, v)=>a.concat(v), [7])
            .map(a=>a*10)
    )

    Obviously toSpliced() method as @Palladium02 and @Bergi noted is far more readable and @AlexanderNenashev's answer the fastest by far. So depending on your need (readability, compatibility, speed or best of both worlds) you may choose any of the answers above.


  2. I used splice, because unshift returns the number of elements in the array. So hoped the result would be [70, 20, 30, 40], but the result was [] as splice returns the elements deleted in the array.

    You’re looking for the new toSpliced method which returns a new array with the modification applied (instead of mutating the receiver and returning the deleted elements):

    console.log(
        [1, 2, 3]
            .map(a=>a+1)
            .toSpliced(0, 0, 7)
            .map(a=>a*10)
    )

    Of course there’s various alternatives to achieve what you want, like .reverse().concat([7]).reverse(), but they’re not very readable.

    Login or Signup to reply.
  3. I would suggest to avoid chaining and thus avoid intermediate arrays (Array#reduce() is a king):

    const map = n => (n+1)*10;
    console.log(
        [1, 2, 3].reduce((r, n, i) => (i ? r.push(map(n)) : r.push(7*10, map(n)), r), [])
    )

    And benchmarking:

    ` Chrome/125
    ----------------------------------------------------------------------------------------------
    >                         n=3        |       n=30       |       n=300       |      n=3000     
    Array#reduce()      ■ 1.00x x10m 197 | ■ 1.00x x10m 866 | ■ 1.00x   x1m 788 | ■ 1.00x x10k 111
    Array#toSpliced()     8.48x  x1m 167 |  10.53x  x1m 912 |  10.63x x100k 838 |   6.07x x10k 674
    ---------------------------------------------------------------------------------------------- `
    

    Open in the playground

    const $chunk = [1,2,3];
    const $input = [], arr = $input;
    
    // @benchmark Array#reduce()
    const map = n => (n+1)*10;
    arr.reduce((r, n, i) => (i ? r.push(map(n)) : r.push(7*10, map(n)), r), [])
    
    // @benchmark Array#toSpliced()
    arr.map(a=>a+1)
            .toSpliced(0, 0, 7)
            .map(a=>a*10)
            
    /*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search