JSのハッシュがクソな理由(あくまで筆者の主観です)
以下の結果は何が出力されるでしょうか?
let hash = {
76: {'name':'太郎A', 'age':'23'},
55: {'name':'太郎B', 'age':'26'},
82: {'name':'太郎C', 'age':'46'}
};
console.log(hash);
大抵の人(筆者を含む)が以下のような結果を期待するのでは無いでしょうか?
{
76: {'name':'太郎A', 'age':'23'},
55: {'name':'太郎B', 'age':'26'},
82: {'name':'太郎C', 'age':'46'}
}
しかし、実際には以下のようになります。
{
55: {'name':'太郎B', 'age':'26'},
76: {'name':'太郎A', 'age':'23'},
82: {'name':'太郎C', 'age':'46'}
}
いやいや、順番変わってるやん。
私は年齢で昇順にデータを持ちたいのです。
どうやらJavaScriptではハッシュがkeyによって昇順ソートされるみたいです。
その代わりキーでデータを取得するのが早そうです。
JavaScriptではハッシュの順序は保証されないらしいです。
キーで昇順になっていましたが、それは実行環境に依存するみたいです。
とりあえずハッシュをやめて配列を使ってみる
順番が変わってしまうため以下のようにデータを持ってみます。
let arr = [
{'id':76, 'name':'太郎A', 'age':'23'},
{'id':55, 'name':'太郎B', 'age':'26'},
{'id':82, 'name':'太郎C', 'age':'46'}
];
console.log(arr);
もちろん想定通りソートされずにデータが取得できます。
しかし、データにアクセスするためにデータを線形検索しないといけなくなります。
let targetId = 55;
for(let v of arr) {
if (targetId === v['id']) {
console.log(`${v['name']}さん、こんにちは。`);
break;
}
}
ちなみにハッシュの場合は以下のように簡単にデータにアクセスできます。
let targetId = 55;
console.log(`${hash[targetId]}さん、こんにちは。`);
こう見るとハッシュはデータのアクセスに便利だなぁ、と思います。
ハッシュと配列のいいとこ取りをしたい
順番は変えずにハッシュのようにデータを取得したい!!!
そんな風に思った時、ハッシュ型の値って参照渡しになっているような気がしたのを思い出しました。
ざっくり言うと以下のような性質です。
let hashA = {'hoge':'ほげ'};
let hashB = hashA;
hashB['hoge'] = 'ホゲ';
console.log(hashA['hoge'])
// 出力結果: ホゲ
この性質を利用し、以下のように設定することでいいとこ取りを実現できました。
let arr = [
{'id':76, 'name':'太郎A', 'age':'23'},
{'id':55, 'name':'太郎B', 'age':'26'},
{'id':82, 'name':'太郎C', 'age':'46'}
];
let hash = {};
for(let v of arr) {
hash[v['id']] = v;
}
上記のように配列とハッシュの両方でデータを持ってやることで
順番を考慮したデータ表示等で利用する場合は配列を、
データを取得して書き換え等を行いたい場合はハッシュを利用することで比較的スマートに記述することができます。
もちろんどちらかの変数でデータの書き換えを行った場合、もう片方にも反映されています。
最後に
JavaScriptでハッシュの並び順が変わることにとても驚きました。
今回私が示したやり方なんかよりもっといいものがあると思います。
もしよければあなたのやり方も共有していただけると幸いです。
一読いただきありがとうございました。
追記
もっといいものがあった!! そう、それはMap
この記事のコメントにあったようにMapは私がやりたかったことそのものでした。
Mapはキー・バリューでデータを保持でき、データをセットした順番も覚えてくれているとのことです。
さっそく今回のケースに合わせて例を書いていきます。
let map = new Map();
map.set(76, {'name':'太郎A', 'age':'23'});
map.set(55, {'name':'太郎B', 'age':'26'});
map.set(82, {'name':'太郎C', 'age':'46'});
for (let [k, v] of map) {
console.log(`${v['name']}さん、こんにちは。`);
}
// 出力結果:
// 太郎Aさん、こんにちは。
// 太郎Bさん、こんにちは。
// 太郎Cさん、こんにちは。
JavaScriptのハッシュは全然クソじゃなかったです。