JavaScript 動的な連想配列の管理方法

JavaScript 動的な連想配列の管理方法

JavaScript のオブジェクトを使うと、場合によっては脆弱性になりうる。
js
Copied!
const data = {
a: 1,
b: 2,
// ...
};

const key = /* ユーザー入力値 */;
// ユーザーが key に "hasOwnProperty" とかを入力してきたら危ない。
console.log(data[key]);


id 付きの Array を使うと、データの探索に計算コストがかかって、連想配列としてのメリットは失われそう。
js
Copied!
const data = [
{ id: "a", value: 1 },
{ id: "b", value: 2 },
];

const key = /* ユーザー入力値 */;
// 探索の計算コストがかかる。
console.log(data.find(({id}) => id === key));


entries data を持っておいて、データを探索したり操作するときだけ Map を使うのはどうか?
Map は JavaScript エンジンがネイティブで実装している。
一時的ではなく、常にデータを Map で持つのもありだと思う。
js
Copied!
const data = [
["a", 1],
["b", 2],
];

const key = /* ユーザー入力値 */;
console.log(new Map(data).get(key));



スピード測定
Node.js v16.13.0
n = 10000
js
Copied!
const indexes = [...Array(10000).keys()];

(() => {
const array = indexes.map((index) => ({ id: index, value: Math.random() }));
const startTime = performance.now();

indexes.forEach((index) => array.find(({ id }) => id === index)?.value);

const endTime = performance.now();

console.log("Array");
console.log(`${endTime - startTime} ms`);
})();

(() => {
const entries = indexes.map((index) => [index, Math.random()]);
const map = new Map(entries);
const startTime = performance.now();

indexes.forEach((index) => map.get(index));

const endTime = performance.now();

console.log("Map を1度だけ生成する");
console.log(`${endTime - startTime} ms`);
})();

(() => {
const entries = indexes.map((index) => [index, Math.random()]);
const startTime = performance.now();

indexes.forEach((index) => new Map(entries).get(index));

const endTime = performance.now();

console.log("Map を毎回生成する");
console.log(`${endTime - startTime} ms`);
})();


結果
bash
Copied!
$ node test.mjs
Array
61.54491899907589 ms
Map を1度だけ生成する
0.4302249997854233 ms
Map を毎回生成する
7529.377149000764 ms


Map を1度だけ生成する 0.4302 ms
143倍
Array 61.54 ms
122 倍
Map を毎回生成する 7529 ms

まあ、パフォーマンスを最適化するのは明らかに動作が遅いときだけにして、
普段は Array でも Map でも書きやすい方法を選んだほうがコードを書く効率良さそう。
Powered by Helpfeel