How to do associative array/hashing in JavaScript

Viewed 805096

I need to store some statistics using JavaScript in a way like I'd do it in C#:

Dictionary<string, int> statistics;

statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);

Is there an Hashtable or something like Dictionary<TKey, TValue> in JavaScript?
How could I store values in such a way?

11 Answers

Use JavaScript objects as associative arrays.

Associative Array: In simple words associative arrays use Strings instead of Integer numbers as index.

Create an object with

var dictionary = {};

JavaScript allows you to add properties to objects by using the following syntax:

Object.yourProperty = value;

An alternate syntax for the same is:

Object["yourProperty"] = value;

If you can, also create key-to-value object maps with the following syntax:

var point = { x:3, y:2 };

point["x"] // returns 3
point.y // returns 2

You can iterate through an associative array using the for..in loop construct as follows

for(var key in Object.keys(dict)){
  var value = dict[key];
  /* use key/value for intended purpose */
}
var associativeArray = {};
associativeArray["one"] = "First";
associativeArray["two"] = "Second";
associativeArray["three"] = "Third";

If you are coming from an object-oriented language you should check this article.

Unless you have a specific reason not to, just use a normal object. Object properties in JavaScript can be referenced using hashtable-style syntax:

var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";

Both foo and bar elements can now then be referenced as:

hashtable['foo'];
hashtable['bar'];

// Or
hashtable.foo;
hashtable.bar;

Of course this does mean your keys have to be strings. If they're not strings they are converted internally to strings, so it may still work. Your mileage may vary.

Since every object in JavaScript behaves like - and is generally implemented as - a hashtable, I just go with that...

var hashSweetHashTable = {};

Note:

Several years ago, I had implemented the following hashtable, which has had some features that were missing to the Map class. However, that's no longer the case — now, it's possible to iterate over the entries of a Map, get an array of its keys or values or both (these operations are implemented copying to a newly allocated array, though — that's a waste of memory and its time complexity will always be as slow as O(n)), remove specific items given their key, and clear the whole map.
Therefore, my hashtable implementation is only useful for compatibility purposes, in which case it'd be a saner approach to write a proper polyfill based on this.


function Hashtable() {

    this._map = new Map();
    this._indexes = new Map();
    this._keys = [];
    this._values = [];

    this.put = function(key, value) {
        var newKey = !this.containsKey(key);
        this._map.set(key, value);
        if (newKey) {
            this._indexes.set(key, this.length);
            this._keys.push(key);
            this._values.push(value);
        }
    };

    this.remove = function(key) {
        if (!this.containsKey(key))
            return;
        this._map.delete(key);
        var index = this._indexes.get(key);
        this._indexes.delete(key);
        this._keys.splice(index, 1);
        this._values.splice(index, 1);
    };

    this.indexOfKey = function(key) {
        return this._indexes.get(key);
    };

    this.indexOfValue = function(value) {
        return this._values.indexOf(value) != -1;
    };

    this.get = function(key) {
        return this._map.get(key);
    };

    this.entryAt = function(index) {

        var item = {};

        Object.defineProperty(item, "key", {
            value: this.keys[index],
            writable: false
        });

        Object.defineProperty(item, "value", {
            value: this.values[index],
            writable: false
        });

        return item;
    };

    this.clear = function() {

        var length = this.length;

        for (var i = 0; i < length; i++) {
            var key = this.keys[i];
            this._map.delete(key);
            this._indexes.delete(key);
        }

        this._keys.splice(0, length);
    };

    this.containsKey = function(key) {
        return this._map.has(key);
    };

    this.containsValue = function(value) {
        return this._values.indexOf(value) != -1;
    };

    this.forEach = function(iterator) {
        for (var i = 0; i < this.length; i++)
            iterator(this.keys[i], this.values[i], i);
    };

    Object.defineProperty(this, "length", {
        get: function() {
            return this._keys.length;
        }
    });

    Object.defineProperty(this, "keys", {
        get: function() {
            return this._keys;
        }
    });

    Object.defineProperty(this, "values", {
        get: function() {
            return this._values;
        }
    });

    Object.defineProperty(this, "entries", {
        get: function() {
            var entries = new Array(this.length);
            for (var i = 0; i < entries.length; i++)
                entries[i] = this.entryAt(i);
            return entries;
        }
    });
}

Documentation of the class Hashtable

Methods:

  • get(key)

    Returns the value associated to the specified key.

    Parameters:
    key: The key from which to retrieve the value.


  • put(key, value)

    Associates the specified value to the specified key.

    Parameters:
    key: The key to which associate the value.
    value: The value to associate to the key.


  • remove(key)

    Removes the specified key, together with the value associated to it.

    Parameters:
    key: The key to remove.


  • clear()

    Clears the whole hashtable, by removing all its entries.


  • indexOfKey(key)

    Returns the index of the specified key, according to the order entries have been added.

    Parameters:
    key: The key of which to get the index.


  • indexOfValue(value)

    Returns the index of the specified value, according to the order entries have been added.

    Parameters:
    value: The value of which to get the index.

    Remarks:
    Values are compared by identity.


  • entryAt(index)

    Returns an object with a key and a value properties, representing the entry at the specified index.

    Parameters:
    index: The index of the entry to get.


  • containsKey(key)

    Returns whether the hashtable contains the specified key.

    Parameters: key: The key to look for.


  • containsValue(value)

    Returns whether the hashtable contains the specified value.

    Parameters:
    value: The value to look for.


  • forEach(iterator)

    Iterates through all the entries in the hashtable, calling specified iterator.

    Parameters:
    iterator: A method with three parameters, key, value and index, where index represents the index of the entry according to the order it's been added.

Properties:

  • length (Read-only)

    Gets the count of the entries in the hashtable.

  • keys (Read-only)

    Gets an array of all the keys in the hashtable.

  • values (Read-only)

    Gets an array of all the values in the hashtable.

  • entries (Read-only)

    Gets an array of all the entries in the hashtable. They're represented the same as the method entryAt().

You can create one using like the following:

var dictionary = { Name:"Some Programmer", Age:24, Job:"Writing Programs"  };

// Iterate over using keys
for (var key in dictionary) {
  console.log("Key: " + key + " , " + "Value: "+ dictionary[key]);
}

// Access a key using object notation:
console.log("Her name is: " + dictionary.Name)

Related