Better array concat...
Eckehard

Eckehard @efpage

About: Always curious...

Location:
Germany
Joined:
May 24, 2021

Better array concat...

Publish Date: Feb 10 '24
7 7

There are different ways to concatenate arrays in Javascript. The most common way is using the spread-syntax:

let a,b,c
a = [1,2,3,4]
b = [5,6,7,8]
c = [...a,...b] // -> [ 1, 2, 3, 4, 5, 6, 7, 8 ]
Enter fullscreen mode Exit fullscreen mode

more ways shown here.

But what, if you need to deal with flexible types?

a = [1,2,3,4]
b = 5
c = [...a,...b] // -> b is not iterable
Enter fullscreen mode Exit fullscreen mode

I came across this problem in a routine, that got passed some variables, that could be either arrays or single values. Doing some type checks would be an option, but how to deal with the different possible cases? Finally I came across a dead simple solution, that does not require any programming at all:

a = [1,2,3,4]
b = 5
c = [a,b,6].flat() // [ 1, 2, 3, 4, 5, 6 ]
Enter fullscreen mode Exit fullscreen mode

The flat-operator flatens only one level, so this works also with nested arrays. If you want deepter flattening, you can specify the depth using flat(n).

Talking of functions, the trick can also be used with function parameters:

const concat = (...x) => x.flat()

a = [1,2,3,4]
b = 5
c = concat(a, b, 6) // -> [ 1, 2, 3, 4, 5, 6 ]
Enter fullscreen mode Exit fullscreen mode

More Advantages of using flat()

By default, Javascript creates "shallow copies" of arrays (copy by reference). This might have unwanted effects:

a = [1,2,3,4]
d = a
d[2]=99 
console.log(a) // -> [ 1, 2, 99, 4 ]
console.log(d) // -> [ 1, 2, 99, 4 ]
Enter fullscreen mode Exit fullscreen mode

Here we changed a without touching it. "a" and "d" share the same data. If we want to create a real copy with separate data, flat(0) is one option:

let a,b,d
a = [1,2,[3,4]]
d = [...a]
d = a.flat(0)
d[1]=99 
console.log(a) // -> [ 1, 2, [ 3, 4 ] ]
console.log(d) // -> [ 1, 99, [ 3, 4 ] ]
Enter fullscreen mode Exit fullscreen mode

Comments 7 total

  • artydev
    artydevFeb 10, 2024

    Thank you Eckehard

  • marco cabrera
    marco cabreraFeb 10, 2024

    Maybe I’m missing something, but could we possibly check if the value is an array first? If it is, using the spread operator seems like a neat way to go. And for single values, couldn’t we just stick to using .push(5) on the array? I’ve always found the push method pretty handy for adding individual values to arrays in JavaScript. What do you think?

    • Eckehard
      EckehardFeb 10, 2024

      You missed the main point: with flat() you do not need to check. Anything is just appended to the array, regardless if it's an array or a simple value.

      • marco cabrera
        marco cabreraFeb 10, 2024

        Ah, I see where you're coming from now. My bad for not catching on quicker. I've always leaned on the flat method for smashing down those layered arrays into one array. Seeing it used in the way you described threw me for a loop—but in a good, 'huh, never thought of it like that' kind of way. It's definitely not the standard route, but hey, if it works, it works. And actually, the more I think about it, the more clever it seems for dodging the whole 'is this a single item or an array?' headache.

        Here's me, noodling around with some code after mulling over what you said:

        
        let a,b
        a = [1,2,3,4]
        b = [5,6,7,8]
        console.log([...a,...b]) // -> [ 1, 2, 3, 4, 5, 6, 7, 8 ]
        
        a.push(...b)
        console.log(a) // push using spread
        
        a = [1,[2],3,4]
        b = [5,6,7,[8]]
        a.push(b)
        console.log(a.flat(Infinity)) // how to flatten all the way down
        
        a = [1,2,3,4]
        b = [5,6,7,8]
        let newArr = a.concat(b)
        console.log(newArr)
        
        a = [1, 2, 3];
        b = [4, 5, 6];
        
        let mergedArray = Array.from({ length: a.length + b.length }, (_, index) =>
            index < a.length ? a[index] : b[index - a.length]
        );
        
        console.log(mergedArray); // Output: [1, 2, 3, 4, 5, 6]
        
        
        Enter fullscreen mode Exit fullscreen mode

        Your comment has definitely broadened my perspective. Thanks for sparking such a fascinating discussion.

        • Eckehard
          EckehardFeb 10, 2024
          • If you mark your codeblocks with ' ' ' JS they get coloured
          • Do you know flems.io? It is a pretty neat way to show your running code and let people play around with it.

          It was more an accident to use flat(). Initially i tried to use this:

          const concat = (...x) => x
          a = [1,2,3,4]
          b = 5
          c = concat(a,b) 
          console.log(c) // -> [ [ 1, 2, 3, 4 ], 5 ]
          
          Enter fullscreen mode Exit fullscreen mode

          But then you come up with a nested array.

          I would prefer to use flat without parameter. flat() ist like flat(1), so it flattens only one level. This let´s you preserve the structure of the initial arrays:

          a = [1,[2,3],4]
          b = [6,[7,8],9]
          
          c = [a,5,b].flat()
          console.log(c) // -> [ 1, [ 2, 3 ], 4, 5, 6, [ 7, 8 ], 9 ]
          
          Enter fullscreen mode Exit fullscreen mode
  • Sean Dinan
    Sean DinanFeb 12, 2024

    I always forget about Array.flat(). There's definitely some Array.isArray(val) ? [...a, ...val] : [...a, val] lines that I could simplify with it. :)

    • Eckehard
      EckehardFeb 13, 2024

      Array.concat() does a similar job, it´s just a matter of taste:

      let a = [1,2,3,4]
      console.log(a.concat(5,6)) // -> [ 1, 2, 3, 4, 5, 6 ]
      
      Enter fullscreen mode Exit fullscreen mode

      It is amazing what is already implemented in the Javascript Arrays. See this overview:

      Array.prototype[@@iterator]()
      Array.prototype.at()
      Array.prototype.concat()
      Array.prototype.copyWithin()
      Array.prototype.entries()
      Array.prototype.every()
      Array.prototype.fill()
      Array.prototype.filter()
      Array.prototype.find()
      Array.prototype.findIndex()
      Array.prototype.findLast()
      Array.prototype.findLastIndex()
      Array.prototype.flat()
      Array.prototype.flatMap()
      Array.prototype.forEach()
      Array.from()
      Array.fromAsync()
      Array.prototype.includes()
      Array.prototype.indexOf()
      Array.isArray()
      Array.prototype.join()
      Array.prototype.keys()
      Array.prototype.lastIndexOf()
      Array.prototype.map()
      Array.of()
      Array.prototype.pop()
      Array.prototype.push()
      Array.prototype.reduce()
      Array.prototype.reduceRight()
      Array.prototype.reverse()
      Array.prototype.shift()
      Array.prototype.slice()
      Array.prototype.some()
      Array.prototype.sort()
      Array.prototype.splice()
      Array.prototype.toLocaleString()
      Array.prototype.toReversed()
      Array.prototype.toSorted()
      Array.prototype.toSpliced()
      Array.prototype.toString()
      Array.prototype.unshift()
      Array.prototype.values()
      Array.prototype.with()
      
      Enter fullscreen mode Exit fullscreen mode
Add comment