Как объединить массив Uint8Arrays?
У меня есть массив, содержащий несколько объектов типа Uint8Array. Он выглядит примерно так:
[Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(16384), Uint8Array(8868)]
Как мне объединить (не уверен, какое точно слово здесь подходит — объединить, конкатенировать или соединить) их в единый ArrayBuffer?
Ключевое здесь в том, что мне необходимо, чтобы на выходе был именно ArrayBuffer.
5 ответ(ов)
Просто относитесь к этому как к любому обычному массиву:
var concatArray = new Uint8Array([ ...Uint8Array1, ...Uint8Array2, ...Uint8Array3 ]);
Таким образом, вы используете оператор расширения (spread) для объединения нескольких Uint8Array
в один массив.
Существует простой способ:
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);
Рекомендуется выбирать подход в зависимости от ваших требований к производительности.
Вы можете использовать метод 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);
Этот код позволяет удобно объединять несколько типизированных массивов в один, сохраняя при этом их данные и порядок.
Вот пример функции, которая объединяет несколько 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
, чтобы вставить каждый массив в нужное место результирующего массива.
Вы можете создать вспомогательную функцию для конкатенации массива массивов произвольной длины в порядке, в котором они были переданы.
Первый вариант выглядит аккуратно, но он создает копию 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()
.
Как перемешать (сделать случайным) массив в JavaScript?
Как объединить два массива в JavaScript и удалить дубликаты?
Как удалить все дубликаты из массива объектов?
Самый быстрый способ дублирования массива в JavaScript: slice против цикла 'for'
Как проверить, содержит ли массив строку в TypeScript?