0

Как объединить массив Uint8Arrays?

14

У меня есть массив, содержащий несколько объектов типа Uint8Array. Он выглядит примерно так:

[Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(8868)]

Как мне объединить (не уверен, какое точно слово здесь подходит — объединить, конкатенировать или соединить) их в единый ArrayBuffer?

Ключевое здесь в том, что мне необходимо, чтобы на выходе был именно ArrayBuffer.

5 ответ(ов)

0

Просто относитесь к этому как к любому обычному массиву:

var concatArray = new Uint8Array([ ...Uint8Array1, ...Uint8Array2, ...Uint8Array3 ]);

Таким образом, вы используете оператор расширения (spread) для объединения нескольких Uint8Array в один массив.

0

Существует простой способ:

await new Blob([a1, a2, a3]).arrayBuffer();

Однако этот способ может быть медленнее:

new Uint8Array(a1.length + a2.length + a3.length)
  .set(a1, 0)
  .set(a2, a1.length)
  .set(a3, a1.length + a2.length);

Рекомендуется выбирать подход в зависимости от ваших требований к производительности.

0

Вы можете использовать метод set. Сначала создайте новый типизированный массив с объединенной длиной всех массивов. Вот пример:

var arrayOne = new Uint8Array([2, 4, 8]);
var arrayTwo = new Uint8Array([16, 32, 64]);

var mergedArray = new Uint8Array(arrayOne.length + arrayTwo.length);
mergedArray.set(arrayOne);
mergedArray.set(arrayTwo, arrayOne.length);

Альтернативный способ: конвертируйте ваши типизированные массивы в обычные массивы, затем объедините их с помощью concat, а в конце создайте новый типизированный массив из объединенного обычного массива.

В вашем случае (решение):

let myArrays = [new Uint8Array(16384), new Uint8Array(16384), new Uint8Array(16384), new Uint8Array(16384), new Uint8Array(16384), new Uint8Array(8868)];

// Получаем общую длину всех массивов.
let length = 0;
myArrays.forEach(item => {
  length += item.length;
});

// Создаем новый массив с общей длиной и объединяем все исходные массивы.
let mergedArray = new Uint8Array(length);
let offset = 0;
myArrays.forEach(item => {
  mergedArray.set(item, offset);
  offset += item.length;
});

// Должен напечатать массив с длиной 90788 (5x 16384 + 8868, ваши исходные массивы)
console.log(mergedArray);

Этот код позволяет удобно объединять несколько типизированных массивов в один, сохраняя при этом их данные и порядок.

0

Вот пример функции, которая объединяет несколько Uint8Array в один, основанный на подходе @Domske:

function mergeUint8Arrays(...arrays) {
  const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
  const merged = new Uint8Array(totalSize);

  arrays.forEach((array, i, arrays) => {
    const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
    merged.set(array, offset);
  });

  return merged;
}

А вот аналогичная реализация на TypeScript:

function mergeUint8Arrays(...arrays: Uint8Array[]): Uint8Array {
  const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
  const merged = new Uint8Array(totalSize);

  arrays.forEach((array, i, arrays) => {
    const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
    merged.set(array, offset);
  });

  return merged;
}

Пример использования:

const arrayOne = new Uint8Array([2, 4, 8]);
const arrayTwo = new Uint8Array([16, 32, 64]);

mergeUint8Arrays(arrayOne, arrayTwo); // Uint8Array(6) [2, 4, 8, 16, 32, 64]

Ваша функция принимает любое количество массивов Uint8Array и объединяет их в один массив. Это достигается путем подсчета общего размера всех входных массивов и использования метода set, чтобы вставить каждый массив в нужное место результирующего массива.

0

Вы можете создать вспомогательную функцию для конкатенации массива массивов произвольной длины в порядке, в котором они были переданы.

Первый вариант выглядит аккуратно, но он создает копию acc на каждой итерации из-за оператора расширения (spread operator):

// массивы – это Uint8Array[]
(arrays) => new Uint8Array(arrays.reduce((acc, curr) => [...acc, ...curr], []));

Хотя это и выглядит проще, производительность ниже, так как acc размножается на каждой итерации.

Второй вариант чуть длиннее, но более производителен, так как acc не размножается при каждой итерации; вместо этого к нему добавляются элементы, и он возвращается:

// массивы – это Uint8Array[]
(arrays) => {
  const flatNumberArray = arrays.reduce((acc, curr) => {
    acc.push(...curr);
    return acc;
  }, []);

  return new Uint8Array(flatNumberArray);
};

Редактирование

Однако вышеописанный метод также создает поверхностную копию curr для каждого массива.

Следующий вариант немного лучше:

// массивы – это Uint8Array[]
(arrays) => {
  const flatNumberArray = arrays.reduce((acc, curr) => {
    acc.push(curr);
    return acc;
  }, []).flat();

  return new Uint8Array(flatNumberArray);
};

Здесь поверхностная копия происходит только один раз, при вызове .flat().

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь