59

I'd like to merge multiple arraybuffers to create a Blob. however, as you know, TypedArray dosen't have "push" or useful methods...

E.g.:

var a = new Int8Array( [ 1, 2, 3 ] );
var b = new Int8Array( [ 4, 5, 6 ] );

As a result, I'd like to get [ 1, 2, 3, 4, 5, 6 ].

2
  • 3
    There's no push because they map to native memory 1:1. Commented Dec 28, 2012 at 15:05
  • ohhh, I understood. TypedArray is not just an array right? thankyou for your comments Commented Dec 28, 2012 at 15:43

8 Answers 8

109

Use the set method. But note, that you now need twice the memory!

var a = new Int8Array( [ 1, 2, 3 ] );
var b = new Int8Array( [ 4, 5, 6 ] );

var c = new Int8Array(a.length + b.length);
c.set(a);
c.set(b, a.length);

console.log(a);
console.log(b);
console.log(c);
Sign up to request clarification or add additional context in comments.

4 Comments

thank you for your reply. i could understand. I need to create a new merged one aint I.
@yomotsu yes you need to create a new one. If you know C, a TypedArray is similar to using malloc (without the need to free). But there's nothing like realloc.
There is a package on NPM for this (npmjs.com/package/concat-typed-array). It took me a while, but I finally figured out that its output matches yours. codesandbox.io/s/… What tripped me up is that I needed to compare the outputs after using .toString().
@Ryan that's because otherwise you're comparing references, which are different (it's a new array, a new pointer to a memory location). To compare array contents you always need to compare them element-by-element. [1] === [1] is false.
11

for client-side ~ok solution:

const a = new Int8Array( [ 1, 2, 3 ] )
const b = new Int8Array( [ 4, 5, 6 ] )
const c = Int8Array.from([...a, ...b])

6 Comments

This will run into length limits. You shouldn't use the spread operator for unbounded data.
It's just the same as this but with longer arrays. Read the documentation.
@Timmmm It works only for arugments of function calls. When using spread syntax for function calls,... and But beware: by using apply this way, you run the risk of exceeding the JavaScript engine's argument length limit also it says argument limit of 65536. I've tested spread with two 65536-length Int8Arrays and it works.
@Timmmm also tested with 2 16777216-length Int8arrays and spread still work with it
The only downside of this less verbose solution is that it creates an intermediate array instead of a typed array. For shorter arrays it won't be a problem, anyway.
|
10

I always use this function:

function mergeTypedArrays(a, b) {
    // Checks for truthy values on both arrays
    if(!a && !b) throw 'Please specify valid arguments for parameters a and b.';  

    // Checks for truthy values or empty arrays on each argument
    // to avoid the unnecessary construction of a new array and
    // the type comparison
    if(!b || b.length === 0) return a;
    if(!a || a.length === 0) return b;

    // Make sure that both typed arrays are of the same type
    if(Object.prototype.toString.call(a) !== Object.prototype.toString.call(b))
        throw 'The types of the two arguments passed for parameters a and b do not match.';

    var c = new a.constructor(a.length + b.length);
    c.set(a);
    c.set(b, a.length);

    return c;
}

The original function without checking for null or types

function mergeTypedArraysUnsafe(a, b) {
    var c = new a.constructor(a.length + b.length);
    c.set(a);
    c.set(b, a.length);

    return c;
}

5 Comments

No null checks, == instead of ===, and this is basically a duplicate of Prinzhorn's answer, wrapped in a function.
Again, just some function I like to use, thought maybe someone else could use it as well - fell free to correct any errors you may find.
One doesn't usually correct errors in an answer's code. That's up to the person that answered the question, hence the comment.
Well I never saw the need to do a strict comparison nor a null check in my use cases, but if it doesn't influence performance negatively, it would make the function more fleshed out.
Yes! Throwing strings is always welcomed of course. I definitely want you as my colleague.
3

if I have multiple typed arrays

            arrays = [ typed_array1, typed_array2,..... typed_array100]

I want concat all 1 to 100 sub array into single 'result' this function works for me.

  single_array = concat(arrays)


function concat(arrays) {
  // sum of individual array lengths
  let totalLength = arrays.reduce((acc, value) => acc + value.length, 0);

  if (!arrays.length) return null;

   let result = new Uint8Array(totalLength);

      // for each array - copy it over result
      // next array is copied right after the previous one
      let length = 0;
      for(let array of arrays) {
            result.set(array, length);
            length += array.length;
      }

      return result;
   }

Comments

1

As a one-liner, which will take an arbitrary number of arrays (myArrays here) and of mixed types so long as the result type takes them all (Int8Array here):

let combined = Int8Array.from(Array.prototype.concat(...myArrays.map(a => Array.from(a))));

Comments

1
function concat (views: ArrayBufferView[]) {
    let length = 0
    for (const v of views)
        length += v.byteLength
        
    let buf = new Uint8Array(length)
    let offset = 0
    for (const v of views) {
        const uint8view = new Uint8Array(v.buffer, v.byteOffset, v.byteLength)
        buf.set(uint8view, offset)
        offset += uint8view.byteLength
    }
    
    return buf
}

Comments

0

For people who love one-liners:

  const binaryData = [
    new Uint8Array([1, 2, 3]),
    new Int16Array([4, 5, 6]),
    new Int32Array([7, 8, 9])
  ];

  const mergedUint8Array = new Uint8Array(binaryData.map(typedArray => [...new Uint8Array(typedArray.buffer)]).flat());

Comments

-1

I like @prinzhorn's answer but I wanted something a bit more flexible and compact:

var a = new Uint8Array( [ 1, 2, 3 ] );
var b = new Float32Array( [ 4.5, 5.5, 6.5 ] );

const merge = (tArrs, type = Uint8Array) => {
  const ret = new (type)(tArrs.reduce((acc, tArr) => acc + tArr.byteLength, 0))
  let off = 0
  tArrs.forEach((tArr, i) => {
    ret.set(tArr, off)
    off += tArr.byteLength
  })
  return ret
}

merge([a, b], Float32Array)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.