現時点でTypedArray
に2次元は実装されていませんが、Array
にTypedArray
を詰め込んだ2次元風TypedArray
を作る事はよくあります。…ないですか? ならばよし!
さて本題、2次元もどきを1次元に変換する処理を考察してみます
let a=[new Uint8Array([1]), new Uint8Array([2,3]), new Uint8Array([4,5,6])],
b=a.flat()
上記のような処理ではbはArrayとなり、TypedArray
ではありません。素朴なloopで実現してみましょう。A.reduce((a,b)=>a+b.length,0)
で要素総数を調べてTypedArray
に突っ込みます
let A=[new Uint8Array([1]), new Uint8Array([2,3]), new Uint8Array([4,5,6])],
B=new Uint8Array(A.reduce((a,b)=>a+b.length,0)),b=0;
for(let a of A)B.set(a,b),b+=a.length;
console.log(B)//[1,2,3,4,5,6]
Blob
を利用すると簡単です。ただしasync
やらawait
やら必要になるのが難点。非同期のnew Blob(a).arrayBuffer().then(a=>{...})
で代用するのもいい
(async a=>{
//2次元もどきTypedArray
a=[new Uint8Array([1]), new Uint8Array([2,3]), new Uint8Array([4,5,6])];
//1次元化
a=new Uint8Array(await new Blob(a).arrayBuffer());
console.log(a)//[1,2,3,4,5,6]
})()
(async a=>{
a=[new Uint16Array([1]), new Uint16Array([11,111]), new Uint16Array([1111,11111,-1])];
a=new Uint16Array(await new Blob(a).arrayBuffer())
console.log(a)//[1,11,111,1111,11111,65535]
})()
速度比較
速度性能はどうなのでしょうか。loopの方はreduce + for of
版とfor of x 2
版を用意。Blob版は前述と同様。
const flat0=A=>{
let B=new Uint8Array(A.reduce((a,b)=>a+b.length,0)),b=0;
for(let a of A)B.set(a,b),b+=a.length;
return B
},
flat1=A=>{let a,b=0,B;
for(a of A)b+=a.length;
B=new Uint8Array(b);b=0;
for(a of A)B.set(a,b),b+=a.length;
return B
},
flat2=async A=>new Uint8Array(await new Blob(A).arrayBuffer());
(async()=>{console.clear();
for(let loop=1<<18,size=1024,n=8;n--;loop>>=1,size*=2){
let A=Array(loop).fill(new Uint8Array(size));
console.time("loop0");
flat0(A);
console.timeEnd("loop0");
console.time("loop1");
flat1(A);
console.timeEnd("loop1");
console.time("blob");
await flat2(A);
console.timeEnd("blob");
}})()
Blob
が他を圧倒する低速ぶりでした。5倍以上遅いですよフフフ…。想定外の結果と言わざるを得ません。flat0とflat1は同程度ですが、flat0(reduce
版)は配列が極端に小さい場合不利っぽいですよフフフ…。
番外編
以下の処理では何が出力されるでしょうか? 3,3,3
? 1,2,3
?
let a=[Array(1),Array(2),Array(3)];
for(a of a)console.log(a.length)
なぜこんな問題出したか…。それは本記事は当初こんな書き方をしようとしていたからだ! 混乱を避けるために改変したらしい…