odiak.net

JavaScriptのSetはオブジェクトを入れづらい

JavaScriptにはSetというクラス(?)があり、一意な値を含んだコレクションを表現できる。 もちろん、プリミティブな値もオブジェクトも入れる事ができる。

プリミティブな値は話が簡単だが、オブジェクトの場合はちと面倒くさい。 オブジェクトの同値関係を自分で定義する事ができないのである。

具体的には、Setに追加される値はObject.isで比較するのとほぼ同じ方法(もっと具体的にはsame-value-zero equality)で既存の値と比較される。 なので、例えば[number, string]のような型のタプルのSetを作っても、同じような値(例えば[2, "bar"])を何度も追加する事ができてしまう。

どうするか。

自分でSetクラスを作る

これは一番確実かもしれない。

ただ、ちゃんと効率的に作るのはそれなりに大変だと思う。 面白そうなのでそのうちやってみたい。

というか、npmにそういうのないのかな?

パフォーマンスが悪くても良ければ、内部を配列にして、挿入するたびに同じ値が入っていないかを確かめるという雑な実装もありかもしれない。

Mapを使う

これはかなり手抜き。 しかも前提条件があって、オブジェクト同士の比較が1つのプリミティブ値の比較になっていること。

例えば、type Obj = {id: string, name: string, count: number}というような型のオブジェクトを、 idさえ同じなら同値とみなすような場合。

この例を使うと、const map = Map<string, Obj>()を用意して、map.set(obj.id, obj)てな感じで値を挿入することで、 idが一意なObjオブジェクトのコレクションが作れる。

まとめ?

JavaScriptのSetオブジェクトが微妙に使いづらいので、なんとかする方法を考えてみた。

Setに関するECMAScriptの仕様も読んでみたいな〜。

あと、MDNのJavaScriptの等価性に関するページが面白かった。(軽く眺めた程度だけど。)
same-value-zero equalityについても今回そのページを見て初めて知った。