Набор объектов в JavaScript - PullRequest
35 голосов
/ 14 апреля 2011

Я хотел бы иметь набор объектов в Javascript.То есть структура данных, которая содержит только уникальные объекты.

Рекомендуется обычно использовать свойства, например, myset["key"] = true.Тем не менее, мне нужны ключи, чтобы быть объектами.Я читал, что Javascript преобразует имена свойств в строки, поэтому я думаю, что я не могу использовать myset[myobject] = true.

Я мог бы использовать массив, но мне нужно что-то лучше, чем производительность O (n) для добавленияпоиск и удаление предметов.

Он должен уметь различать объекты только по ссылке, поэтому, учитывая:

var a = {};
var b = {};

, тогда и a, и b должны иметь возможностьдобавил, потому что они являются отдельными объектами.

По сути, я хотел что-то вроде C ++ std::set, который может хранить объекты Javascript.Есть идеи?

Ответы [ 8 ]

12 голосов
/ 14 апреля 2011

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
7 голосов
/ 15 апреля 2011

Вот безумное предложение ... наберите его в результате JSON.stringify(object)

7 голосов
/ 14 апреля 2011

Это возможно не для всех объектов, но если в вашем объекте реализован метод .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';
5 голосов
/ 15 апреля 2011

Я отвечаю на свой вопрос, но я нашел альтернативное решение, которое показалось мне интересным, и подумал, что было бы полезно поделиться им.

Ответ Кволков дал мне идею. При условии, что метод toString() объекта однозначно идентифицирует экземпляр, свойства объекта можно использовать для хранения набора объектов. По сути, для хранения объекта x вы можете использовать items[x.toString()] = x;. Обратите внимание, что значением является сам объект, поэтому набор объектов можно извлечь, просмотрев все свойства item и выгрузив все значения в массив.

Вот класс, который я называю ObjectSet, полностью. Это требует, чтобы объекты были однозначно идентифицированы их методом toString(), который подходит для моих целей. add, remove и contains должны работать быстрее, чем O (n) - независимо от эффективности доступа к свойствам 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;
};
4 голосов
/ 14 апреля 2011

Для того, что вы пытаетесь сделать (наборы объектов), нет встроенной реализации Javascript. Вы должны будете реализовать это самостоятельно. Один из способов сделать это - реализовать функцию хеширования для ваших объектов. Поддерживающим типом данных набора будет ассоциативный массив, где ключом массива является значение, которое вы получаете от вызова хеш-функции объекта, а значением массива является сам объект.

Конечно, это не решает проблему, которую вы выдвинули на первый план, поэтому вам также необходимо учитывать равенство (возможно, реализовать функцию равенства)?

Вместо того, чтобы делать хеш-функцию свойством самого объекта, вы можете иметь автономную хеш-функцию, которая принимает объект в качестве входных данных и генерирует хеш-значение (предположительно, перебирая его свойства).

Используя этот метод, вы сможете получить O(1) для вставки, поиска и удаления (не считая порядка хеш-функции, который не должен быть хуже, чем O(n), особенно если вы выполняете итерацию по его свойства для создания вашего хэшированного значения).

3 голосов

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

0 голосов
/ 16 февраля 2014

Кажется, что внутренний вызов функции работает, если префикс this .Пример:

var put;
this.put = put = function(x) {
    if (!this.contains(x))
        list.push(x);

    return this;
}
0 голосов
/ 14 апреля 2011

Просто набрал это, это только кратко проверено:

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;
}
...