【问题标题】:How can I merge TypedArrays in JavaScript?如何在 JavaScript 中合并 TypedArrays?
【发布时间】:2021-11-17 21:21:10
【问题描述】:

我想合并多个数组缓冲区来创建一个 Blob。然而,如你所知, TypedArray 没有“推送”或有用的方法...

例如:

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

因此,我想得到[ 1, 2, 3, 4, 5, 6 ]

【问题讨论】:

  • 没有push,因为它们以 1:1 的比例映射到本机内存。
  • 哦,我明白了。 TypedArray 不仅仅是一个数组,对吧?谢谢你的cmets

标签: javascript typed-arrays


【解决方案1】:

使用set 方法。但请注意,您现在需要两倍的内存!

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

【讨论】:

  • 感谢您的回复。我能理解。我需要创建一个新的合并的不是我。
  • @yomotsu 是的,您需要创建一个新的。如果您了解 C,TypedArray 类似于使用malloc(不需要free)。但没有什么比得上realloc
  • NPM 上有一个包可以解决这个问题 (npmjs.com/package/concat-typed-array)。我花了一段时间,但我终于发现它的输出与你的匹配。 codesandbox.io/s/… 让我大吃一惊的是,我需要在使用 .toString() 之后比较输出。
  • @Ryan 那是因为否则你正在比较不同的引用(它是一个新数组,一个指向内存位置的新指针)。要比较数组内容,您总是需要逐个元素地比较它们。 [1] === [1] 是假的。
【解决方案2】:

对于客户端~ok 解决方案:

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

【讨论】:

  • 这将遇到长度限制。您不应该将扩展运算符用于无限数据。
  • 与此相同,但数组更长。 Read the documentation.
  • @Timmmm 它仅适用于函数调用的参数。 When using spread syntax for function calls,...But beware: by using apply this way, you run the risk of exceeding the JavaScript engine's argument length limit 还说 argument limit of 65536. 我已经用两个 65536 长度的 Int8Arrays 测试了传播,它可以工作。
  • @Timmmm 还使用 2 个 16777216 长度的 Int8array 进行了测试,并且 spread 仍然可以使用它
  • 这种不太冗长的解决方案的唯一缺点是它创建了一个中间 array 而不是类型化数组。无论如何,对于较短的数组,这不是问题。
【解决方案3】:

我一直使用这个功能:

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;
}

不检查 null 或类型的原始函数

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

    return c;
}

【讨论】:

  • No null 检查,== 而不是 ===,这基本上是 Prinzhorn 答案的副本,包含在一个函数中。
  • 再说一次,只是我喜欢使用的一些功能,我想也许其他人也可以使用它 - 请随时纠正您可能发现的任何错误。
  • 通常不会纠正答案代码中的错误。这取决于回答问题的人,因此是评论。
  • 嗯,我从来没有看到需要在我的用例中进行严格比较或空值检查,但如果它不会对性能产生负面影响,它会使函数更加充实。
  • 是的!当然,总是欢迎扔弦。我绝对希望你成为我的同事。
【解决方案4】:

如果我有多个类型化数组

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

我想将所有 1 到 100 个子数组连接成单个“结果” 这个功能对我有用。

  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;
   }

【讨论】:

    【解决方案5】:

    作为一个单行,它将采用任意数量的数组(此处为myArrays)和混合类型,只要结果类型全部采用它们(此处为Int8Array):

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

    【讨论】:

      【解决方案6】:

      对于喜欢单线的人:

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

      【讨论】:

        【解决方案7】:
        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
        }
        

        【讨论】:

          【解决方案8】:

          我喜欢@prinzhorn 的回答,但我想要一些更灵活和紧凑的东西:

          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)
          

          【讨论】:

            猜你喜欢
            • 2020-04-03
            • 2021-10-30
            • 1970-01-01
            • 2021-06-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-16
            • 2012-11-30
            相关资源
            最近更新 更多