skip to Main Content

I’m trying to figure out the best way to to insert an element into a list.

I’ve come up with several approaches:

// setup
let messages = Array.from({ length: 1e5 }, (_, index) => ({ text: `text for message ${index + 1}` }));

The first tests don’t clone the original list.

const prependMessage1 = () => {
    messages = [{ text:'test message 1' }, ...messages];
};

prependMessage1();

// 531 ops/s ± 4.61%
// 99.5 % slower
const prependMessage2 = () => {
    messages = [{ text:'test message 2' }].concat(messages);
};

prependMessage2();

// 5.6K ops/s ± 1.49%
// 94.71 % slower
const prependMessage3 = () => {
    messages.unshift({ text: 'text message 3' })
};

prependMessage3();
// 105K ops/s ± 1.25%
// Fastest ✅
const prependMessage4 = () => {
    messages.reverse();
    messages.push({ text: 'text message 4' });
    messages.reverse();
};

prependMessage4();

// 4.9K ops/s ± 1.08%
// 95.3 % slower

I’m using a state manager in my app that doesn’t allow the mutation of the state directly. Each method should return a new object, not mutate the current list of messages. Now what’s interesting is that these results change once I start cloning:

const prependMessage1 = () => {
    const copy = [...messages];
    messages = [{ text:'test message 1' }, ...copy];
};

prependMessage1();

// 472 ops/s ± 3.19%
// 84.57 % slower
const prependMessage2 = () => {
    const copy = [...messages];
    messages = [{ text:'test message 2' }].concat(copy);
};

prependMessage2();

// 3.1K ops/s ± 1.09%
// Fastest ✅
const prependMessage3 = () => {
    const copy = [...messages];
    copy.unshift({ text: 'text message 3' })
    messages = copy;
};

prependMessage3();

// 2.5K ops/s ± 2.28%
// 19.7 % slower
const prependMessage4 = () => {
    const copy = [...messages];
    copy.reverse();
    copy.push({ text: 'text message 4' });
    copy.reverse();
    messages = copy;
};

prependMessage4();

// 1.2K ops/s ± 1.11%
// 61.14 % slower

Based on these tests, is it safe to assume that I should be using concat? As the list grows, it seems as though concat wins here. Or perhaps there’s another most optimal way that I’m missing?

2

Answers


  1. So, based on these tests, it looks like using concat is a pretty solid choice for slapping a new element onto the start of your array, especially when you’re cloning things.

    The numbers show that concat is a champ in various situations, beating out the other methods most of the time.

    And hey, since your state manager is all strict about not messing directly with the state and wants a shiny new object each time, concat fits the bill perfectly.

    But, you know, don’t forget that real-life performance might throw a few surprises, and it’s not just about speed.

    How your code reads and how easy it is to keep up are also important.

    So, keep an eye on how things run in your app and do some regular testing to make sure everything’s smooth.

    Hope you doing well, thanks.

    Login or Signup to reply.
  2. Yes, seems concat() is the fastest. Btw you don’t need to copy the messages array in the case of concat() and the spread syntax – they create a new copy.

    ` Chrome/120
    ------------------------------------------------------------
    concat              1.00x  |  x1000  164  170  174  174  182
    Array::toSpliced    1.09x  |  x1000  179  185  187  192  203
    for                 1.41x  |  x1000  231  231  236  239  247
    Array::unshift      2.71x  |  x1000  444  450  466  472  484
    spread              3.94x  |  x1000  646  653  655  658  660
    Array.from         16.71x  |   x100  274  280  281  281  281
    ------------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    ` Firefox/120
    -----------------------------------------------------------
    Array::toSpliced   1.00x  |  x1000  220  230  244  246  271
    concat             1.05x  |  x1000  232  234  234  237  238
    Array::unshift     1.38x  |  x1000  303  308  310  312  320
    for                1.70x  |  x1000  375  396  398  413  425
    spread             3.30x  |  x1000  726  727  727  733  735
    Array.from         4.73x  |   x100  104  107  107  108  110
    -----------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const messages = Array.from({ length: 1e5 }, (_, index) => ({ text: `text for message ${index + 1}` }));
    
    // @benchmark for
    const arr = Array(messages.length + 1);
    arr[0] = { text:'test message 1' };
    for(let i = 0; i < messages.length; i++){
      arr[i + 1] = messages[i];
    }
    arr;
    
    // @benchmark concat
    [{ text:'test message 1' }].concat(messages);
    
    // @benchmark Array::unshift
    {
    const arr = messages.slice();
    arr.unshift({ text:'test message 1' });
    arr;
    }
    
    // @benchmark spread
    [{ text:'test message 1' }, ...messages];
    
    // @benchmark Array.from
    Array.from({length: messages.length + 1}, (_, idx) => idx ? messages[idx - 1] : { text:'test message 1' });
    
    // @benchmark Array::toSpliced
    
    messages.toSpliced(0, 0, { text:'test message 1' });
    
    /*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search