JavaScript
JSON
連想配列

JavaScriptで、オブジェクトに値を紐付けたい

JavaScriptで、キーと値の対応を取るような構造を考えていたときに、少し悩むことがありました。

他言語と比較して

RubyやJavaでは、Hashのキーには文字列や数値だけでなく、任意のオブジェクトを入れることができます。そして、「オブジェクトとしての同一性」以外に「内容の同一性」を考える、別次元での等値判定が用意してあります。

一方で、JavaScriptには連想配列を実現する手段として、「Object」と「Map」があります。詳しい記事があるので中身は省略すると、

  • Object…キーは文字列しか使えない、toString__proto__のように特殊な動作をするキーもある
  • Map…オブジェクト型についてはオブジェクトの同一性判定しかない

という状況なので、(JavaScriptにそもそもオブジェクトが「値として同一」だということを判定する仕組み自体ないですが)「値として同一」なオブジェクトをキーに処理を行いたい、となると、なかなかうまく行きません。

JSON化の罠

幸い、今回キーにしたいオブジェクトは、「Object」「文字列」「配列」「数値」「ブール値」「null」を組み合わせた、JSONにできるオブジェクトなので、「JSONをキーにすれば適当な方法で管理できるんじゃないか」とも、いったんは思いました。

ただ、RubyやPHPでは連想配列の順序が保証されるのに対して、JavaScriptもJSONも、キーの順序は不定となっています。つまり、「キーも値もすべて同じ」オブジェクトがあったとして、それをJSON化しても、JSONがどんな順序になるのか制御する方法もないわけで、単純な手法では同じJSONを生成することができません。

// falseとなる可能性が高いけど、それも保証されない
console.log(JSON.stringify({foo: '1', bar: 2}) === JSON.stringify({bar: 2, foo: '1'}));

探せばあった

そこで行き詰まっていたところ、npmにjson.sortifyというものがありました。名前のとおり、キーをソートしてJSONを生成するルーチンで、「A deterministic version of JSON.stringify」、つまり同じキーと値の組み合わせなら必ず同じJSON文字列を生成する、とありました。今回やりたいことにぴったりです。

…とおもったら、JavaScriptとしての規定がないからわざわざ入れようというはずなのに、「先に入れたキーが先に出力される」というJavaScriptエンジンの挙動に依存していて、「これはどうなんだ」という気分になってしまいました。