Convert javascript array to Float32Array to buffer and Back - javascript

I have a JS array with floats like so [0.0028808217, -0.027968751, -0.029748825] and I transform it to a Float32 Buffer with the following function:
function toFloat32Buffer(array) {
return Buffer.from(new Float32Array(array).buffer)
}
The issue I'm facing is making the conversion from the Float32 Buffer back to a JS array, so I can use the same values as the original array.
I've tried
function toJSArray(buffer) {
const newBuffer = Buffer.from(buffer)
const floatArray = new Float32Array(newBuffer)
return Array.from(floatArray)
}
function toFloat32Buffer(arr) {
return Buffer.from(new Float32Array(arr).buffer)
}
const array = [0.0028808217, -0.027968751, -0.029748825]
const bufferFromFloat32 = toFloat32Buffer(array);
const array2 = toJSArray(bufferFromFloat32);
console.log('array', array)
console.log('array2', array2)
array and array2 have different values. How should I transform back the buffer to get the same values as the original array? I'm running this in NodeJS

This is alternative solution. Considering using alloc
function bufferToArray(buffer, size) {
let arr = [];
for (let i = 0; i < buffer.length / size; i++)
arr.push(buffer.readDoubleLE(i * size));
return arr;
}
function arrayToBuffer(arr, size) {
const buffer = Buffer.allocUnsafe(size * arr.length);
arr.map((val, index) => buffer.writeDoubleLE(val, size * index));
return buffer;
}
let array = [0.0028808217, -0.027968751, -0.029748825];
const size = Float64Array.BYTES_PER_ELEMENT; // double-precision
const buffer = arrayToBuffer(array, size);
const array2 = bufferToArray(buffer, size);
console.log(array); // [0.0028808217, -0.027968751, -0.029748825]
console.log(array2); // [0.0028808217, -0.027968751, -0.029748825]

Rewrite your toJsArray function to:
function toJSArray(buffer) {
return new Float32Array(buffer.buffer);
}
The buffer property of buffer returns the underlying ArrayBuffer of the buffer.
But as #Matt mentioned in his comment:
Javascript numbers are double-precision 64-bit. I don't think you can
get back the precision that is removed converting to 32bit, short of
storing it somewhere else. – Matt
For more details: buff.buffer

Related

Create a functions, which creates a new array out of an existing one, which has less or equal amount of elements and no repeats

I have an array like this:
var arr = [0,1,2,3,4,5]
What I need is to write a function, which will generate a new array of random length (but less or equal than arr.length) and won't generate repeated numbers. Something like:
var arrNew = [2,4,0]
but not
var arrNew = [2,4,2,2,0]
I came up with this, but it generates repeated elements:
var list = [0,1,2,3,4,5];
var getRandomNumberRange = function (min, max) {
return Math.floor(Math.random() * (max - min) + min);
};
var getRandomArr = function (list) {
var arr = new Array(getRandomNumberRange(1, 6));
for (var i = 0; i < arr.length; i++) {
arr[i] = list[getRandomNumber(list.length)];
}
return arr;
};
Appreciate your help!
NOTE: there's nothing stopping this code from giving you an empty array as your random array.
var arr = [0,1,2,3,4,5, 36, 15, 25]
function generateRandomArrayFromSeed(arr) {
// Create a new set to keep track of what values we've used
const used = new Set();
// generate a new array.
const newArr = [];
// what random length will we be using today?
const newLength = getRandomNumber(arr.length);
// while our new array is still less than our newly randomized length, we run this block of code
while(newArr.length < newLength) {
// first, we generate a random index
const randomNumber = getRandomNumber(arr.length);
// then we check if the value at this index is in our set; if it is, we continue to the next iteration of the loop, and none of the remaining code is ran.
if(used.has(arr[randomNumber])) continue;
// otherwise, push this new value to our array
newArr.push(arr[randomNumber]);
// and update our used set by adding our random number
used.add(arr[randomNumber]);
}
// finally return this new random array.
return newArr;
}
function getRandomNumber(limit = 10) {
return Math.floor(Math.random() * limit);
}
console.log(generateRandomArrayFromSeed(arr));
Moving the data vrom an array datastructure to a Set could solve the problem.
Sets dont accept duplicate values.
For more on Set in javascript, see MDN here
To convert the Set back to an array, use Array.from()
To avoid duplicates in you final array just use Set and convert it back to an array using Array.from
Save you result inside set as below:
arr = Array.from(new Set(<result array>));

Convert number to a reversed array of digits

I've developed this codepen (http://codepen.io/PiotrBerebecki/pen/qZjojV?editors=0010) trying to solve the following JavaScript problem:
Given a non-negative integer, return an array containing a list of independent digits in reverse order.
Example:
348597 => The correct solution should be [7,9,5,8,4,3]
The function below apparently is incorrect as it returns ["7", "9", "5", "8", "4", "3"] - correct order but with quotes. How could I modify it so that it returns [7,9,5,8,4,3]?
function digitize(n) {
var initialArray = (""+n).split('');
var reversedArray = [];
for (var i = initialArray.length - 1; i >= 0; i--) {
reversedArray[i] = initialArray.shift();
}
return reversedArray;
}
"One-line" solution:
var num = 348597,
arr = String(num).split("").reverse().map(Number);
console.log(arr); // [7, 9, 5, 8, 4, 3]
String(num) : The String global object acts as a constructor for strings and "converts" the given number into string(in this case)
The Array.reverse(): method reverses an array in place
The Array.map(): method creates and returns a new array calling a provided function on every array element
add parseInt to convert from string to number, since when you split you turn every integer into a string
function digitize(n) {
var initialArray = (""+n).split('');
var reversedArray = [];
for (var i = initialArray.length - 1; i >= 0; i--) {
reversedArray[i] = parseInt(initialArray.shift(),10);
}
return reversedArray;
}
console.log(digitize(348597));
Even better, reduce it to two lines:
function digitize(num) {
return num.toString().split('').reverse().map(Number);
}
The final map call applies a function to each element in the array (in this case the function converts a string to an object) - everything else simply converts the number to a string, splits the string to an array and reverses it.
Traditionally, parseInt would be used in the map call, but this gives rise to strange behaviour.
Just split and reverse
var num = 348597,
arr = num.toString().split("").reverse().map(Number);
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');
It is a simplified version of many other solutions that you can see, to deep understand how it works.
function digitize(n) {
let correctArr = [];
let arrOfNumbers = n.toString().split('');
let arrOfNumbersLength = arrOfNumbers.length;
for (let i = 0; i < arrOfNumbersLength; i++) {
let x = arrOfNumbers.pop();
correctArr.push(+x);
}
return correctArr;
}
console.log(digitize(348597));
If you care about performance, here is the one.
var num = 348597;
var digits = num + '';
var result = [];
for (var i = 0, length = digits.length; i < length; i++) {
result[length - 1 - i] = +digits[i];
}
console.log(result);
For beginners who may want to see a clear format on how to solve this via plain English, this may help to understand it:
function reverseNumber(num){
num = num + '';
let reversedText = num.split('').reverse().join('');
let reversedNumber = parseInt(reversedText, 10);
console.log("reversed number: ", reversedNumber);
return reversedNumber;
}
console.log(reverseNumber(12345));
A simpler way to solve this is below.
function digitize(n) {
numbers = n.toString()//convert n to a string
arrayNum = numbers.split('') //split the string and make an array
arrayRev =arrayNum.reverse()//reverse the new array made.
newArr = arrayRev.map(Number) // The Number constructor contains constants and methods for working with numbers. Values of other types can be converted to numbers using the Number() function.
return newArr;
}
Refactored
function digitize(n) {
return n.toString().split('').reverse().map(Number)
}

How to append bytes, multi-bytes and buffer to ArrayBuffer in javascript?

Javascript ArrayBuffer or TypedArrays dont have any kind of appendByte(), appendBytes(), or appendBuffer() methods. So if I want to fill an ArrayBuffer one value at a time, how do I do it?
var firstVal = 0xAB; // 1 byte
var secondVal = 0x3D7F // 2 bytes
var anotherUint8Array = someArr;
var buffer = new ArrayBuffer(); // I don't know the length yet
var bufferArr = new UInt8Array(buffer);
// following methods do not exist. What are the alternatives for each??
bufferArr.appendByte(firstVal);
bufferArr.appendBytes(secondVal);
bufferArr.appendBuffer(anotherUint8Array);
You can create a new TypedArray with a new ArrayBuffer, but you can't change the size of an existing buffer
function concatTypedArrays(a, b) { // a, b TypedArray of same type
var c = new (a.constructor)(a.length + b.length);
c.set(a, 0);
c.set(b, a.length);
return c;
}
Now can do
var a = new Uint8Array(2),
b = new Uint8Array(3);
a[0] = 1; a[1] = 2;
b[0] = 3; b[1] = 4;
concatTypedArrays(a, b); // [1, 2, 3, 4, 0] Uint8Array length 5
If you want to use different types, go via Uint8Array as the smallest unit is a byte, i.e.
function concatBuffers(a, b) {
return concatTypedArrays(
new Uint8Array(a.buffer || a),
new Uint8Array(b.buffer || b)
).buffer;
}
This means .length will work as expected, you could now convert this to your typed array of choice (make sure it's a type that would accept the .byteLength of the buffer though)
From here, you could now implement any method you like for concatenating your data, e.g.
function concatBytes(ui8a, byte) {
var b = new Uint8Array(1);
b[0] = byte;
return concatTypedArrays(ui8a, b);
}
var u8 = new Uint8Array(0);
u8 = concatBytes(u8, 0x80); // [128]
Paul's answer allows you to concatenate one TypedArray to an existing TypedArray. In ES6, you can use the following function to concatenate multiple TypedArrays:
function concatenate(resultConstructor, ...arrays) {
let totalLength = 0;
for (const arr of arrays) {
totalLength += arr.length;
}
const result = new resultConstructor(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
const ta = concatenate(Uint8Array,
Uint8Array.of(1, 2), Uint8Array.of(3, 4));
console.log(ta); // Uint8Array [1, 2, 3, 4]
console.log(ta.buffer.byteLength); // 4
To append a new byte is:
const byte = 3;
concatenate(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(byte));
This method is found in ExploringJS.

Sampling a random subset from an array

What is a clean way of taking a random sample, without replacement from an array in javascript? So suppose there is an array
x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
and I want to randomly sample 5 unique values; i.e. generate a random subset of length 5. To generate one random sample one could do something like:
x[Math.floor(Math.random()*x.length)];
But if this is done multiple times, there is a risk of a grabbing the same entry multiple times.
I suggest shuffling a copy of the array using the Fisher-Yates shuffle and taking a slice:
function getRandomSubarray(arr, size) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(0, size);
}
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var fiveRandomMembers = getRandomSubarray(x, 5);
Note that this will not be the most efficient method for getting a small random subset of a large array because it shuffles the whole array unnecessarily. For better performance you could do a partial shuffle instead:
function getRandomSubarray(arr, size) {
var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min);
}
A little late to the party but this could be solved with underscore's new sample method (underscore 1.5.2 - Sept 2013):
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var randomFiveNumbers = _.sample(x, 5);
In my opinion, I do not think shuffling the entire deck necessary. You just need to make sure your sample is random not your deck. What you can do, is select the size amount from the front then swap each one in the sampling array with another position in it. So, if you allow replacement you get more and more shuffled.
function getRandom(length) { return Math.floor(Math.random()*(length)); }
function getRandomSample(array, size) {
var length = array.length;
for(var i = size; i--;) {
var index = getRandom(length);
var temp = array[index];
array[index] = array[i];
array[i] = temp;
}
return array.slice(0, size);
}
This algorithm is only 2*size steps, if you include the slice method, to select the random sample.
More Random
To make the sample more random, we can randomly select the starting point of the sample. But it is a little more expensive to get the sample.
function getRandomSample(array, size) {
var length = array.length, start = getRandom(length);
for(var i = size; i--;) {
var index = (start + i)%length, rindex = getRandom(length);
var temp = array[rindex];
array[rindex] = array[index];
array[index] = temp;
}
var end = start + size, sample = array.slice(start, end);
if(end > length)
sample = sample.concat(array.slice(0, end - length));
return sample;
}
What makes this more random is the fact that when you always just shuffling the front items you tend to not get them very often in the sample if the sampling array is large and the sample is small. This would not be a problem if the array was not supposed to always be the same. So, what this method does is change up this position where the shuffled region starts.
No Replacement
To not have to copy the sampling array and not worry about replacement, you can do the following but it does give you 3*size vs the 2*size.
function getRandomSample(array, size) {
var length = array.length, swaps = [], i = size, temp;
while(i--) {
var rindex = getRandom(length);
temp = array[rindex];
array[rindex] = array[i];
array[i] = temp;
swaps.push({ from: i, to: rindex });
}
var sample = array.slice(0, size);
// Put everything back.
i = size;
while(i--) {
var pop = swaps.pop();
temp = array[pop.from];
array[pop.from] = array[pop.to];
array[pop.to] = temp;
}
return sample;
}
No Replacement and More Random
To apply the algorithm that gave a little bit more random samples to the no replacement function:
function getRandomSample(array, size) {
var length = array.length, start = getRandom(length),
swaps = [], i = size, temp;
while(i--) {
var index = (start + i)%length, rindex = getRandom(length);
temp = array[rindex];
array[rindex] = array[index];
array[index] = temp;
swaps.push({ from: index, to: rindex });
}
var end = start + size, sample = array.slice(start, end);
if(end > length)
sample = sample.concat(array.slice(0, end - length));
// Put everything back.
i = size;
while(i--) {
var pop = swaps.pop();
temp = array[pop.from];
array[pop.from] = array[pop.to];
array[pop.to] = temp;
}
return sample;
}
Faster...
Like all of these post, this uses the Fisher-Yates Shuffle. But, I removed the over head of copying the array.
function getRandomSample(array, size) {
var r, i = array.length, end = i - size, temp, swaps = getRandomSample.swaps;
while (i-- > end) {
r = getRandom(i + 1);
temp = array[r];
array[r] = array[i];
array[i] = temp;
swaps.push(i);
swaps.push(r);
}
var sample = array.slice(end);
while(size--) {
i = swaps.pop();
r = swaps.pop();
temp = array[i];
array[i] = array[r];
array[r] = temp;
}
return sample;
}
getRandomSample.swaps = [];
Or... if you use underscore.js...
_und = require('underscore');
...
function sample(a, n) {
return _und.take(_und.shuffle(a), n);
}
Simple enough.
You can get a 5 elements sample by this way:
var sample = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
.map(a => [a,Math.random()])
.sort((a,b) => {return a[1] < b[1] ? -1 : 1;})
.slice(0,5)
.map(a => a[0]);
You can define it as a function to use in your code:
var randomSample = function(arr,num){ return arr.map(a => [a,Math.random()]).sort((a,b) => {return a[1] < b[1] ? -1 : 1;}).slice(0,num).map(a => a[0]); }
Or add it to the Array object itself:
Array.prototype.sample = function(num){ return this.map(a => [a,Math.random()]).sort((a,b) => {return a[1] < b[1] ? -1 : 1;}).slice(0,num).map(a => a[0]); };
if you want, you can separate the code for to have 2 functionalities (Shuffle and Sample):
Array.prototype.shuffle = function(){ return this.map(a => [a,Math.random()]).sort((a,b) => {return a[1] < b[1] ? -1 : 1;}).map(a => a[0]); };
Array.prototype.sample = function(num){ return this.shuffle().slice(0,num); };
While I strongly support using the Fisher-Yates Shuffle, as suggested by Tim Down, here's a very short method for achieving a random subset as requested, mathematically correct, including the empty set, and the given set itself.
Note solution depends on lodash / underscore:
Lodash v4
const _ = require('loadsh')
function subset(arr) {
return _.sampleSize(arr, _.random(arr.length))
}
Lodash v3
const _ = require('loadsh')
function subset(arr) {
return _.sample(arr, _.random(arr.length));
}
If you're using lodash the API changed in 4.x:
const oneItem = _.sample(arr);
const nItems = _.sampleSize(arr, n);
https://lodash.com/docs#sampleSize
A lot of these answers talk about cloning, shuffling, slicing the original array. I was curious why this helps from a entropy/distribution perspective.
I'm no expert but I did write a sample function using the indexes to avoid any array mutations — it does add to a Set though. I also don't know how the random distribution on this but the code was simple enough to I think warrant an answer here.
function sample(array, size = 1) {
const { floor, random } = Math;
let sampleSet = new Set();
for (let i = 0; i < size; i++) {
let index;
do { index = floor(random() * array.length); }
while (sampleSet.has(index));
sampleSet.add(index);
}
return [...sampleSet].map(i => array[i]);
}
const words = [
'confused', 'astonishing', 'mint', 'engine', 'team', 'cowardly', 'cooperative',
'repair', 'unwritten', 'detailed', 'fortunate', 'value', 'dogs', 'air', 'found',
'crooked', 'useless', 'treatment', 'surprise', 'hill', 'finger', 'pet',
'adjustment', 'alleged', 'income'
];
console.log(sample(words, 4));
Perhaps I am missing something, but it seems there is a solution that does not require the complexity or potential overhead of a shuffle:
function sample(array,size) {
const results = [],
sampled = {};
while(results.length<size && results.length<array.length) {
const index = Math.trunc(Math.random() * array.length);
if(!sampled[index]) {
results.push(array[index]);
sampled[index] = true;
}
}
return results;
}
Here is another implementation based on Fisher-Yates Shuffle. But this one is optimized for the case where the sample size is significantly smaller than the array length. This implementation doesn't scan the entire array nor allocates arrays as large as the original array. It uses sparse arrays to reduce memory allocation.
function getRandomSample(array, count) {
var indices = [];
var result = new Array(count);
for (let i = 0; i < count; i++ ) {
let j = Math.floor(Math.random() * (array.length - i) + i);
result[i] = array[indices[j] === undefined ? j : indices[j]];
indices[j] = indices[i] === undefined ? i : indices[i];
}
return result;
}
You can remove the elements from a copy of the array as you select them. Performance is probably not ideal, but it might be OK for what you need:
function getRandom(arr, size) {
var copy = arr.slice(0), rand = [];
for (var i = 0; i < size && i < copy.length; i++) {
var index = Math.floor(Math.random() * copy.length);
rand.push(copy.splice(index, 1)[0]);
}
return rand;
}
For very large arrays, it's more efficient to work with indexes rather than the members of the array.
This is what I ended up with after not finding anything I liked on this page.
/**
* Get a random subset of an array
* #param {Array} arr - Array to take a smaple of.
* #param {Number} sample_size - Size of sample to pull.
* #param {Boolean} return_indexes - If true, return indexes rather than members
* #returns {Array|Boolean} - An array containing random a subset of the members or indexes.
*/
function getArraySample(arr, sample_size, return_indexes = false) {
if(sample_size > arr.length) return false;
const sample_idxs = [];
const randomIndex = () => Math.floor(Math.random() * arr.length);
while(sample_size > sample_idxs.length){
let idx = randomIndex();
while(sample_idxs.includes(idx)) idx = randomIndex();
sample_idxs.push(idx);
}
sample_idxs.sort((a, b) => a > b ? 1 : -1);
if(return_indexes) return sample_idxs;
return sample_idxs.map(i => arr[i]);
}
My approach on this is to create a getRandomIndexes method that you can use to create an array of the indexes that you will pull from the main array. In this case, I added a simple logic to avoid the same index in the sample. this is how it works
const getRandomIndexes = (length, size) => {
const indexes = [];
const created = {};
while (indexes.length < size) {
const random = Math.floor(Math.random() * length);
if (!created[random]) {
indexes.push(random);
created[random] = true;
}
}
return indexes;
};
This function independently of whatever you have is going to give you an array of indexes that you can use to pull the values from your array of length length, so could be sampled by
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
getRandomIndexes(myArray.length, 3).map(i => myArray[i])
Every time you call the method you are going to get a different sample of myArray. at this point, this solution is cool but could be even better to sample different sizes. if you want to do that you can use
getRandomIndexes(myArray.length, Math.ceil(Math.random() * 6)).map(i => myArray[i])
will give you a different sample size from 1-6 every time you call it.
I hope this has helped :D
Underscore.js is about 70kb. if you don't need all the extra crap, rando.js is only about 2kb (97% smaller), and it works like this:
console.log(randoSequence([8, 6, 7, 5, 3, 0, 9]).slice(-5));
<script src="https://randojs.com/2.0.0.js"></script>
You can see that it keeps track of the original indices by default in case two values are the same but you still care about which one was picked. If you don't need those, you can just add a map, like this:
console.log(randoSequence([8, 6, 7, 5, 3, 0, 9]).slice(-5).map((i) => i.value));
<script src="https://randojs.com/2.0.0.js"></script>
D3-array's shuffle uses the Fisher-Yeates shuffle algorithm to randomly re-order arrays. It is a mutating function - meaning that the original array is re-ordered in place, which is good for performance.
D3 is for the browser - it is more complicated to use with node.
https://github.com/d3/d3-array#shuffle
npm install d3-array
//import {shuffle} from "d3-array"
let x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
d3.shuffle(x)
console.log(x) // it is shuffled
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
If you don't want to mutate the original array
let x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let shuffled_x = d3.shuffle(x.slice()) //calling slice with no parameters returns a copy of the original array
console.log(x) // not shuffled
console.log(shuffled_x)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>

What's the most straightforward way to copy an ArrayBuffer object?

I'm working with ArrayBuffer objects, and I would like to duplicate them. While this is rather easy with actual pointers and memcpy, I couldn't find any straightforward way to do it in Javascript.
Right now, this is how I copy my ArrayBuffers:
function copy(buffer)
{
var bytes = new Uint8Array(buffer);
var output = new ArrayBuffer(buffer.byteLength);
var outputBytes = new Uint8Array(output);
for (var i = 0; i < bytes.length; i++)
outputBytes[i] = bytes[i];
return output;
}
Is there a prettier way?
I prefer the following method
function copy(src) {
var dst = new ArrayBuffer(src.byteLength);
new Uint8Array(dst).set(new Uint8Array(src));
return dst;
}
It appears that simply passing in the source dataview performs a copy:
var a = new Uint8Array([2,3,4,5]);
var b = new Uint8Array(a);
a[0] = 6;
console.log(a); // [6, 3, 4, 5]
console.log(b); // [2, 3, 4, 5]
Tested in FF 33 and Chrome 36.
ArrayBuffer is supposed to support slice (http://www.khronos.org/registry/typedarray/specs/latest/) so you can try,
buffer.slice(0);
which works in Chrome 18 but not Firefox 10 or 11. As for Firefox, you need to copy it manually. You can monkey patch the slice() in Firefox because the Chrome slice() will outperform a manual copy. This would look something like,
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
var that = new Uint8Array(this);
if (end == undefined) end = that.length;
var result = new ArrayBuffer(end - start);
var resultArray = new Uint8Array(result);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i + start];
return result;
}
Then you can call,
buffer.slice(0);
to copy the array in both Chrome and Firefox.
Hmmm... if it's the Uint8Array you want to slice (which logically, it should be), this may work.
if (!Uint8Array.prototype.slice && 'subarray' in Uint8Array.prototype)
Uint8Array.prototype.slice = Uint8Array.prototype.subarray;
Faster and slightly more complicated version of chuckj's answer. Should use ~8x less copy operations on large Typed Arrays. Basically we copy as much 8-byte chunks as possible and then copy the remaining 0-7 bytes. This is especially useful in current version of IE, since it doesn't have slice method implemented for ArrayBuffer.
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
if (end == undefined) end = that.length;
var length = end - start;
var lengthDouble = Math.floor(length / Float64Array.BYTES_PER_ELEMENT);
// ArrayBuffer that will be returned
var result = new ArrayBuffer(length);
var that = new Float64Array(this, start, lengthDouble)
var resultArray = new Float64Array(result, 0, lengthDouble);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
// copying over the remaining bytes
that = new Uint8Array(this, start + lengthDouble * Float64Array.BYTES_PER_ELEMENT)
resultArray = new Uint8Array(result, lengthDouble * Float64Array.BYTES_PER_ELEMENT);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
return result;
}
Wrap a Buffer around the ArrayBuffer. This is shared memory and no copy is made. Then create a new Buffer from the wrapping Buffer. This will copy the data. Finally get a reference to the new Buffer's ArrayBuffer.
This is the most straighforward way I can find. The most efficient? Perhaps.
const wrappingBuffer = Buffer.from(arrayBuffer)
const copiedBuffer = Buffer.from(wrappingBuffer)
const copiedArrayBuffer = copiedBuffer.buffer
In some cases (like webaudio Audiobuffers) you only have a reference to the 2 arrays.
So if you have array1 as a float32Array and array2 as a float32Array,
you must do an element by element copy.
To do so you can use different methods.
var ib=z.inputBuffer.getChannelData(0);
var ob=z.outputBuffer.getChannelData(0);
this
ib.forEach((chd,i)=>ob[i]=chd);
or this nicer and probably faster
ob.set(ib);
That's because Array.set populates an existing array with multiple data (even from another array)
Some of the operations above only do "shallow" copies. When working with workers and transferable arrays, you need to do a deep copy.
function copyTypedArray(original, deep){
var copy;
var kon = original.constructor;
if(deep){
var len = original.length;
copy = new kon(len);
for (var k=len; --k;) {
copy[k] = original[k];
}
} else {
var sBuf = original.buffer;
copy = new kon(sBuf);
copy.set(original);
}
return copy;
}
HINT (for the confused): Typed Arrays contain an ArrayBuffer, which can be obtained via the "buffer" property.
var arr = new Float32Array(8);
arr.buffer <-- this is an ArrayBuffer
If you're in the browser you can do:
const copy = structuredClone(buffer);

Categories