Javascriptでオブジェクトのセットが欲しいです。つまり、一意のオブジェクトのみを含むデータ構造。
通常、プロパティを使用することをお勧めします。 myset["key"] = true
。ただし、キーをオブジェクトにする必要があります。 Javascriptがプロパティ名を文字列にキャストすることを読んだので、myset[myobject] = true
を使用できないと思います。
配列を使用することもできますが、項目の追加、検索、および削除のパフォーマンスは、O(n)よりも優れたものが必要です。
参照のみでオブジェクトを区別できる必要があるので、次のようにします。
var a = {};
var b = {};
a
とb
の両方を追加できるようにする必要があります。これらは別個のオブジェクトであるためです。
基本的に、Javascriptオブジェクトを格納できるC++のstd::set
のようなものを探しています。何か案は?
ES6はネイティブ Set
を提供します:
let s = new Set();
let a = {};
let b = {};
s.add(a);
console.log(s.has(a)); // true
console.log(s.has(b)); // false
すべてのオブジェクトで使用できるわけではありませんが、オブジェクトに.toString()
メソッドが実装されている場合、次のようになります。
var x = {toString: function(){ return 'foo'; }};
var y = {toString: function(){ return 'bar'; }};
var obj = {};
obj[x] = 'X';
obj[y] = 'Y';
console.log(obj);
// { foo: 'X', bar: 'Y' }
これをもっと簡単にしたい場合は、クラスにしてください:
function myObj(name){
this.name = name;
}
myObj.prototype.toString = function(){ return this.name; }
var obj = {};
obj[new myObj('foo')] = 'X';
obj[new myObj('bar')] = 'Y';
ここに気違いの提案があります... JSON.stringify(object)
の結果にそれをキー
私は自分の質問に答えていますが、私は面白いと思い、それを共有することが有用だと思った代替ソリューションを思いつきました。
cwolvesの答えは私にアイデアを与えてくれました。オブジェクトのtoString()
メソッドを提供すると、インスタンスが一意に識別され、オブジェクトのプロパティを使用してオブジェクトのセットを格納できます。基本的に、オブジェクトx
を保存するには、items[x.toString()] = x;
を使用できます。値はオブジェクト自体であるため、すべてのitem
のプロパティを確認し、すべての値を配列にダンプすることにより、オブジェクトのセットを抽出できます。
ここにクラスを示します。これをObjectSet
と呼びます。オブジェクトはtoString()
メソッドによって一意に識別される必要がありますが、これは私の目的には問題ありません。 add
、remove
、およびcontains
はすべて、O(n) time-javascriptのプロパティアクセス効率が何であれ、どのような場合でもO(1)またはO(n log n)。
// Set of objects. Requires a .toString() overload to distinguish objects.
var ObjectSet = function ()
{
this.items = {};
this.item_count = 0;
};
ObjectSet.prototype.contains = function (x)
{
return this.items.hasOwnProperty(x.toString());
};
ObjectSet.prototype.add = function (x)
{
if (!this.contains(x))
{
this.items[x.toString()] = x;
this.item_count++;
}
return this;
};
ObjectSet.prototype.remove = function (x)
{
if (this.contains(x))
{
delete this.items[x.toString()];
this.item_count--;
}
return this;
};
ObjectSet.prototype.clear = function ()
{
this.items = {};
this.item_count = 0;
return this;
};
ObjectSet.prototype.isEmpty = function ()
{
return this.item_count === 0;
};
ObjectSet.prototype.count = function ()
{
return this.item_count;
};
ObjectSet.prototype.values = function ()
{
var i, ret = [];
for (i in this.items)
{
if (this.items.hasOwnProperty(i))
ret.Push(this.items[i]);
}
return ret;
};
あなたがやろうとしていること(オブジェクトのセット)のために、ネイティブのJavascript実装はありません。これを自分で実装する必要があります。これを行う1つの方法は、オブジェクトのハッシュ関数を実装することです。セットのバッキングデータ型は連想配列になります。配列のキーはオブジェクトのハッシュ関数の呼び出しから取得した値であり、配列の値はオブジェクト自体です。
もちろん、これはあなたが強調した問題に対処していないので、平等も考慮する必要があります(おそらく等機能を実装します)?
ハッシュ関数をオブジェクト自体のプロパティにする代わりに、オブジェクトを入力として受け取り、ハッシュ値を生成するスタンドアロンのハッシュ関数を使用できます(おそらくプロパティを反復処理することによって)。
このメソッドを使用すると、O(1)
を挿入、検索、削除することができます(ハッシュ関数の順序はカウントしません。特に[繰り返し]を使用している場合は、O(n)
より悪くないはずです)そのプロパティを介してハッシュ値を作成します)。
ECMAScript6 Set
は次のように動作するはずです。
Firefox 32での作業例(ただし、Chromium 37では実装されていません):
if (Set) {
var s = new Set()
var a = {}
var b = {}
var c = {}
s.add(a)
s.add(b)
s.add(b)
assert(s.size === 2)
assert(s.has(a))
assert(s.has(b))
assert(!s.has(c))
}
{} != {}
:同等では、デフォルトでオブジェクトアドレスが比較されます。
サポートなしのブラウザ用に実装するモジュール: https://github.com/medikoo/es6-set
thisを前に付けると、関数の内部呼び出しが機能するようです。例:
var put;
this.put = put = function(x) {
if (!this.contains(x))
list.Push(x);
return this;
}
これを入力するだけで、簡単にテストされます。
var Set = function Set()
{
var list = [];
var contains;
this.contains = contains = function(x) {
return list.indexOf(x) >= 0;
}
var put;
this.put = put = function(x) {
if (!contains(x))
list.Push(x);
return this;
}
var remove;
this.remove = remove = function(x)
{
var idx = list.indexOf(x);
if (idx >= 0)
list.splice(idx,1);
return this;
}
var all;
this.all = all = function()
{
return list.concat();
}
return this;
}