JavaScript 動的な連想配列の管理方法
JavaScript のオブジェクトを使うと、場合によっては脆弱性になりうる。
jsconst data = { a: 1, b: 2, // ...};
const key = /* ユーザー入力値 */;// ユーザーが key に "hasOwnProperty" とかを入力してきたら危ない。console.log(data[key]);
id 付きの Array を使うと、データの探索に計算コストがかかって、連想配列としてのメリットは失われそう。jsconst data = [ { id: "a", value: 1 }, { id: "b", value: 2 },];
const key = /* ユーザー入力値 */;// 探索の計算コストがかかる。console.log(data.find(({id}) => id === key));
entries で
data を持っておいて、データを探索したり操作するときだけ Map を使うのはどうか? Map は JavaScript エンジンがネイティブで実装している。
一時的ではなく、常にデータを Map で持つのもありだと思う。
jsconst data = [ ["a", 1], ["b", 2],];
const key = /* ユーザー入力値 */;console.log(new Map(data).get(key));
スピード測定
Node.js v16.13.0
n = 10000
jsconst 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$ node test.mjs Array61.54491899907589 msMap を1度だけ生成する0.4302249997854233 msMap を毎回生成する7529.377149000764 ms
Map を1度だけ生成する 0.4302 ms
143倍
Array 61.54 ms
122 倍
Map を毎回生成する 7529 ms
まあ、パフォーマンスを最適化するのは明らかに動作が遅いときだけにして、
普段は Array でも Map でも書きやすい方法を選んだほうがコードを書く効率良さそう。