16

I want to use a data structure in JavaScript that can be used to store number of IDs. I should be able to check if a key already exists in that set, something like Java Sets.

I want to achive same behaviours as follows (this code is in Java):

Set<String> st = new HashSet<String>();
//add elemets

if(st.contains("aks") ){
  //do something
}

I want a JavaScript/dojo equivalent of the above code.

Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
aks
  • 223
  • 4
  • 5
  • 8

6 Answers6

20

I've written a JavaScript HashSet implementation that does what you want and allows any object to be a member of the set: http://code.google.com/p/jshashtable

However, if you just need to store strings, you could do something more simply by storing set members as property names of a normal Object. For example:

function StringSet() {
    var setObj = {}, val = {};

    this.add = function(str) {
        setObj[str] = val;
    };

    this.contains = function(str) {
        return setObj[str] === val;
    };

    this.remove = function(str) {
        delete setObj[str];
    };

    this.values = function() {
        var values = [];
        for (var i in setObj) {
            if (setObj[i] === val) {
                values.push(i);
            }
        }
        return values;
    };
}

A note about the implementation: val is an object used internally by the StringSet implementation that is unique to each set. Comparing property values of the object whose property names make up the set (setObj) against val eliminates the need for a hasOwnProperty() check and guarantees that only strings that have been added to the set will show up in values.

Example usage:

var set = new StringSet();
set.add("foo");
set.add("bar");

alert(set.contains("foo")); // true
alert(set.contains("baz")); // false

set.values(); // ["foo", "bar"], though not necessarily in that order
set.remove("foo");
set.values(); // ["bar"]
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 1
    What is the purpose of avoiding the call to hasOwnProperty()? Is the equality check faster? If so, how do you know? – Jørgen Fogh Apr 07 '14 at 10:55
  • @JørgenFogh: I suspect the equality check is likely to be faster because it avoids the overhead of a function call. I haven't tested this though, so feel free to create a benchmark; I'd be interested to see the results. – Tim Down Apr 07 '14 at 14:16
  • @JørgenFogh: The other advantage of the object comparison is that there is no way it can go wrong. – Tim Down Apr 07 '14 at 14:20
11

Why not use a normal object and check if a key exists with JavaScript's hasOwnProperty?

var x = {};
x['key'] = 'val';
x.hasOwnProperty('key'); // true //
x.hasOwnProperty('key2'); // false //

And here is a more advanced use case:

var x = {};
var prefix = 'item_';
for(var i=0;i<10;i++){
   x[prefix+i] = 'value '+(i+1);
}
x.hasOwnProperty('item_6'); // true //
x.hasOwnProperty('other key'); // false //

Removing items can be done like this:

delete x['key'];
Alin Purcaru
  • 43,655
  • 12
  • 77
  • 90
  • thanks for replying: I want to dynamically add many keys first to object, then later i want to check if a key exists in the object. how can i do that? – aks Dec 03 '10 at 08:48
  • @The keys can be any string (even a variable that contains a string). I'll update with an example. – Alin Purcaru Dec 03 '10 at 08:50
  • can i do like this : var key1= "xyz" ; x[key1] = "val" ; and then check using ur code? – aks Dec 03 '10 at 08:51
  • @ask Yes. The key can be anything that evaluates to a string: a string literal, a variable, an expression (as I have in the second example) or any object (because it will be converted to a string). – Alin Purcaru Dec 03 '10 at 08:55
  • How do you delete an element? – G B Dec 03 '10 at 08:56
  • 1
    @G B With `delete x['key'];`. And again the key can be anything. I used a literal just for convenience. – Alin Purcaru Dec 03 '10 at 08:58
  • Why do you use hasOwnProperty() method? It makes code more difficult to read and it's not nessary for this case. Just check: – Alexandr Dec 03 '10 at 09:32
  • @Alexander `hasOwnProperty` is the only reliable way to check if an object has a certain key set. Comparing to `null` or `undefined` may be OK in particular cases when you are sure your items will not take those values, but I wanted my answer to target a wider audience. – Alin Purcaru Dec 03 '10 at 09:34
  • As a rule if you define your object via {}, but not via new SomeClass(); call, then you don't consider working with fields via prototypes. But even if you add such fields, then it does matter and you may read the fields just via x["item_6"]. – Alexandr Dec 03 '10 at 09:39
  • There's no need for a `hasOwnProperty()` check if you assign a value that is unique to the set to each property. See my answer. – Tim Down Dec 03 '10 at 09:57
  • @Tim You're right. I misunderstood the question a bit. I was under the (wrong) impression that he needs key-value pairs. But if his set values are strings I think he should stick with a plain Object. – Alin Purcaru Dec 03 '10 at 10:14
  • @Alin Purcaru.Can i use your solution.AM bit cofused by your last comment – aks Dec 03 '10 at 10:20
  • @aks If the values you need to store are strings you can and it will be the most straight forward solution. With the note that you can do `x[myId] = true` and check with `if(x[myId])`. – Alin Purcaru Dec 03 '10 at 10:24
  • @Alin: Wrapping a plain object seems simpler to me, given that your way would still seem to require a `hasOwnProperty()` check. – Tim Down Dec 03 '10 at 10:35
  • @Tim A `set[item] == true` would work as well if adding is done with `x[item] = true`, so `hasOwnProperty` will be avoided. – Alin Purcaru Dec 03 '10 at 10:39
  • 1
    Not necessarily: `Object.prototype.foo = "true"; alert(x["foo"] == true)` – Tim Down Dec 03 '10 at 11:03
  • @Tim You're right. I didn't take that into account. It seems that with my approach you're stuck with using `hasOwnProperty`. – Alin Purcaru Dec 03 '10 at 11:10
  • There's a typo in my previous comment: there's incorrect quotes around "true", but I can't edit it now and deleting it would destroy the context. – Tim Down Dec 03 '10 at 16:51
  • 1
    If you're creating a simple object and prototypes are not an issue, just use the 'in' operator. It reads much nicer. And static lookups like x["foo"] can be written x.foo. – peller Dec 03 '10 at 17:25
  • @peller: Sure. Not sure why I wrote `x["foo"]` up there. Probably because there were lots of other square brackets around. – Tim Down Dec 03 '10 at 17:29
4

No Dojo needed, this is native to Javascript. Use Objects. Sounds like you only need the keys, not the values. Lookup is constant time.

var st = {'aks':1, 'foo':1, 'bar':1};  // or could start with empty {}. 1 could be any value of any type, it's just short.

//add elements
st.baz = 1;

//or load up dynamically

myArrayOfStrings.forEach(function(key){
 st[key] = 1;
});


if("aks" in st){
  //do something
}
peller
  • 4,435
  • 19
  • 21
  • `forEach` isn't supported in IE. This also needs a `hasOwnProperty()` check in case some other code has done something like `Object.prototype.aks = 1`. – Tim Down Dec 04 '10 at 00:25
  • Yes, in my example I'm assuming ES5 and that nobody is modifying the Object prototype, which is best practice. – peller Dec 05 '10 at 15:18
0

Possibly with an associative array / Hashtable / dictionary (I don't know how it's called exactly), using the set elements as keys and "anything else" as values.

insert: mySet[key] = "Whatever";

delete: mySet[key] = null;

check: if (mySet[key] != null) { ... }
Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
G B
  • 2,951
  • 2
  • 28
  • 50
0

Hash is good candidate for implementing Set. You could create a set using a function like that:

function set () {
    var result = {};
    for (var i = 0; i < arguments.length; i++) result[arguments[i]] = true;
    return result;
}

For instance:

x = set([1,2,2,4])
x[1] #==> true
x[3] #==> false
x[5] = true; # add element to the set
x[5] = false; # remove element from the set
fifigyuri
  • 5,771
  • 8
  • 30
  • 50
0

Sets don't have keys. They only have set of values, but maps have pairs of key/value entities.

As a result, you have 2 options. Each of them has its drawbacks and advantages:

  1. You can use as described above JavaScript object. Actually it is a map/associative array/hash table. One of its advantage - you can guarantee with this kind of structure that keys - are unique items. Its drawback connected to the issue - you have to keep some extra information that you don't need at all. Values of maps. trues or some other values. It does not matter. Why do you need them?

  2. To resolve the previous disadvantage you may consider using JavaScript arrays. But, you'll have to write some wrappers so arrays's behavior will look like sets behavior. Also operations that will search by the uniqueId will be slower than the same ones for hashtables cause you'll have to iterate via all items of an array.

So, I think you should prefer hashtables to arrays, examples you can find in other posts. But probably you should consider changing of your data structure. don't keep uniqueId as keys with unselerss values if its possible. Let your unique ids point to some real objects for which these unique ids are used.

PS: one more thing. Arrays are also objects actually. As a result they can be used as hashtables/maps too.

Alexandr
  • 9,213
  • 12
  • 62
  • 102