web-dev-qa-db-fra.com

Ensemble d'objets en javascript

Je voudrais avoir un ensemble d'objets en Javascript. Autrement dit, une structure de données qui ne contient que des objets uniques.

Il est généralement recommandé d'utiliser des propriétés, par exemple myset["key"] = true. Cependant, j'ai besoin que les clés soient des objets. J'ai lu que Javascript convertit les noms de propriété en chaînes, donc je suppose que je ne peux pas utiliser myset[myobject] = true.

Je pourrais utiliser un tableau, mais j'ai besoin de quelque chose de mieux que O(n) performances pour ajouter, rechercher et supprimer des éléments.

Il doit être capable de distinguer les objets uniquement par référence, donc étant donné:

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

alors a et b devraient pouvoir être ajoutés, car ce sont des objets distincts.

Fondamentalement, je recherche quelque chose comme std::set En C++, qui peut stocker des objets Javascript. Des idées?

36
AshleysBrain

ES6 fournit un natif Set :

let s = new Set();
let a = {};
let b = {};

s.add(a);

console.log(s.has(a));  // true
console.log(s.has(b));  // false
16
Ry-

Ce n'est pas possible pour tous les objets, mais si votre objet a une méthode .toString() implémentée, c'est:

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' }

Si vous voulez rendre cela plus facile, faites-en une classe:

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';
7
Mark Kahn

Voici une folle suggestion ... saisissez-la sur le résultat de JSON.stringify(object)

7
Jim Blackler

Je réponds à ma propre question, mais j'ai trouvé une solution alternative que j'ai trouvée intéressante et qui aurait été utile de la partager.

la réponse des cwolves m'a donné une idée. Si la méthode toString() d'un objet identifie de manière unique l'instance, les propriétés d'un objet peuvent être utilisées pour stocker un ensemble d'objets. Essentiellement, pour stocker l'objet x, vous pouvez utiliser items[x.toString()] = x;. Notez que la valeur est l'objet lui-même, donc l'ensemble d'objets peut être extrait en regardant toutes les propriétés de item et en vidant toutes les valeurs dans un tableau.

Voici la classe, que j'appelle ObjectSet, en entier. Cela nécessite que les objets soient identifiés de manière unique par leur méthode toString(), ce qui est bien pour moi. add, remove et contains devraient tous s'exécuter mieux que O(n) temps - quelle que soit l'efficacité d'accès à la propriété de javascript, ce qui, espérons-le, est soit O(1) ou 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;
};
5
AshleysBrain

Pour ce que vous essayez de faire (ensembles d'objets), il n'y a pas d'implémentation Javascript native. Vous devez le mettre en œuvre vous-même. Une façon de le faire serait d'implémenter une fonction de hachage pour vos objets. Le type de données de support de l'ensemble serait un tableau associatif, où la clé du tableau est la valeur que vous obtenez en appelant la fonction de hachage de l'objet, et la valeur du tableau est l'objet lui-même.

Bien sûr, cela ne résout pas le problème que vous avez mis en évidence, vous devrez donc également prendre en compte l'égalité (mettre en œuvre une fonction d'égalité peut-être)?

Au lieu de faire de la fonction de hachage une propriété de l'objet lui-même, vous pouvez avoir une fonction de hachage autonome qui prend un objet en entrée et génère une valeur de hachage (probablement en itérant sur ses propriétés).

En utilisant cette méthode, vous devriez pouvoir obtenir O(1) pour l'insertion, la recherche et la suppression (sans compter l'ordre de la fonction de hachage, qui ne devrait pas être pire que O(n), en particulier si vous parcourez ses propriétés pour créer votre valeur hachée).

4
Vivin Paliath

ECMAScript6 Set devrait se comporter comme ça:

Exemple de travail sur Firefox 32 (mais non implémenté dans 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))
}

Ce n'est pas surprenant puisque {} != {}: l'égalité compare les adresses des objets par défaut.

Un module qui l'implémente pour les navigateurs sans support: https://github.com/medikoo/es6-set

Il semble que l'appel interne de fonction fonctionne lorsqu'il est préfixé par this. Exemple:

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

    return this;
}
0
Aurelien

Je viens de taper ceci, il n'est que brièvement testé:

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;
}
0
Sophistifunk