Array.prototype.reduce()で1つのオブジェクトを使いまわす実装が速い。
data.reduce((acc, obj) => {
acc[obj.id] = obj;
return acc;
}, {});
配列からオブジェクトへ変換したい
JavaScriptで、何かをキーとして配列からオブジェクトに変換したいことってよくありますよね。典型的な例は、IDの入った次のようなオブジェクトの配列をJSONで受け取った時などです。
[
{ id: 0, value: "val_0" },
{ id: 1, value: "val_1" },
{ id: 2, value: "val_2" },
...
]
これを次の形式のオブジェクトに変換すると、色々と都合が良いです。
{
0: { id: 0, value: "val_0" },
1: { id: 1, value: "val_1" },
2: { id: 2, value: "val_2" },
...
}
何かいい実装方法があるのではと思い「JavaScript 配列 オブジェクト 変換」でググったところ色々出てきましたが、ユーティリティ的に使用する上ではどうしても性能が気になります。そこで今回はいくつかの実装について、実行速度を計測してみました。
配列→オブジェクト変換 実装3選
方法1: Object.fromEntries()
Object.fromEntries(data.map(obj => [obj.id, obj]))
キーと値のペアの配列にして、オブジェクトに変換します。
ちなみにObject.fromEntries()はES2019以降で使用できます。
方法2: Array.prototype.reduce() + オブジェクト使いまわし
data.reduce((acc, obj) => {
acc[obj.id] = obj;
return acc;
}, {});
空のオブジェクトを初期値として、キーと値のペアを追加します。1つのオブジェクトを使いまわしています。
ちなみにArray.prototype.reduce()はES2015(ES6)以降で使用できます。
方法3: Array.prototype.reduce() + スプレッド構文
data.reduce((acc, obj) => ({
...acc,
[obj.id]: obj
}), {});
空のオブジェクトを初期値として、スプレッドした返値にキーと値のペアを追加します。方法2とは少し異なり、スプレッドによって一時オブジェクトを毎回生成しています。
ちなみにオブジェクトのスプレッド構文はES2018以降で使用できます。
性能測定
JavaScriptコードの実行速度を計測・比較できるMeasureThat.netで上記のコードを実行しました。サンプルデータおよび出力は記事冒頭と同じ形式で、配列長は10000としています。環境はWindows 10, Chrome 99です。次のリンクからお手元のWebブラウザでも計測可能なので、気になった方はお試しください。
測定結果
実装 | Executions per second(1秒間に実行できる回数) |
---|---|
Object.fromEntries() | 4775.2 Ops/sec |
Array.prototype.reduce() + オブジェクト使いまわし | 6923.0 Ops/sec |
Array.prototype.reduce() + スプレッド構文 | 48.3 Ops/sec |
方法2(reduce+オブジェクト使いまわし)が速いという結果になりました。メモリ使用量も簡易的に計測してみましたが、どの方法も大差ありませんでした。やはり方法3のようなオブジェクトの逐次生成はダメですね。
まとめ
JavaScriptの比較的新しい(ES5やES6以降、いわばポストIE)仕様・構文は非常に強力ですから、これからも知識を深めていきましょう。