198

How can you create the JavaScript/JQuery equivalent of this Java code:

Map map = new HashMap(); //Doesn't not have to be a hash map, any key/value map is fine
map.put(myKey1, myObj1);
map.put(myKey2, myObj2); //Repeat n times

function Object get(k) {
    return map.get(k);
}
Marcus Leon
  • 55,199
  • 118
  • 297
  • 429

6 Answers6

303

Edit: Out of date answer, ECMAScript 2015 (ES6) standard javascript has a Map implementation, read here for more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

var map = new Object(); // or var map = {};
map[myKey1] = myObj1;
map[myKey2] = myObj2;

function get(k) {
    return map[k];
}

//map[myKey1] == get(myKey1);
basickarl
  • 37,187
  • 64
  • 214
  • 335
Simen Echholt
  • 11,243
  • 2
  • 34
  • 26
  • 12
    Updated my answer to use a `new Object()` instead of `new Array()`. See [this article](http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/) – Simen Echholt Nov 22 '10 at 15:55
  • How can we provide a root node here? – RK- Jun 15 '11 at 20:28
  • 1
    How can we remove a key value payer from the map ? – Shikha Dhawan Jan 29 '13 at 05:37
  • @Shikha Most efficient way would simply be to put (key, null) and then check if your value is null. Otherwise, check how to remove items in an array (using javascript function splice) – Adrien Gorrell Apr 18 '13 at 08:45
  • Is myKey1 a variable? Is it a string? There are no quotes around it. Can there be quotes around it if I want that as the key? – Yster Apr 06 '15 at 11:41
  • 4
    This answer is now out of date as Map does exist now (yay!), read the following: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map – basickarl Jul 25 '16 at 01:29
  • The problem of the official implementation is that is doesn't allow custom equality (it only uses ===) so I still have to write my own. – Yolomep Oct 07 '20 at 16:36
79

Just use plain objects:

var map = { key1: "value1", key2: "value2" }
function get(k){
  return map[k];
}
Madeyedexter
  • 1,155
  • 2
  • 17
  • 33
Simon
  • 3,509
  • 18
  • 21
25
function Map() {
    this.keys = new Array();
    this.data = new Object();

    this.put = function (key, value) {
        if (this.data[key] == null) {
            this.keys.push(key);
        }
        this.data[key] = value;
    };

    this.get = function (key) {
        return this.data[key];
    };

    this.remove = function (key) {
        this.keys.remove(key);
        this.data[key] = null;
    };

    this.each = function (fn) {
        if (typeof fn != 'function') {
            return;
        }
        var len = this.keys.length;
        for (var i = 0; i < len; i++) {
            var k = this.keys[i];
            fn(k, this.data[k], i);
        }
    };

    this.entrys = function () {
        var len = this.keys.length;
        var entrys = new Array(len);
        for (var i = 0; i < len; i++) {
            entrys[i] = {
                key: this.keys[i],
                value: this.data[i]
            };
        }
        return entrys;
    };

    this.isEmpty = function () {
        return this.keys.length == 0;
    };

    this.size = function () {
        return this.keys.length;
    };
}
DanielST
  • 13,783
  • 7
  • 42
  • 65
Jade
  • 267
  • 3
  • 3
  • 4
    At least, put all those methods on the prototype. – Bergi May 18 '13 at 14:36
  • 4
    @Adshi: [Use of 'prototype' vs. 'this' in Javascript?](https://stackoverflow.com/questions/310870/use-of-prototype-vs-this-in-javascript) – Bergi Feb 19 '14 at 16:34
  • 4
    `this.data[key] = null` won't delete the property; it just sets its value to `null`. You should use `delete this.data[key]` instead. – Michael Scheper Apr 04 '14 at 00:49
  • At entry function, you should change like this. entrys[i] = { key: this.keys[i], value: this.data[i] }; -> entrys[i] = { key: this.keys[i], value: this.data[this.keys[i]] }; - – Kyung Hwan Min Sep 11 '15 at 21:09
  • @Bergi this < prototype < local scope (currying), believe me, life is much easier when you don't have to deal with leaking variables and changable scope. – yyny Apr 11 '16 at 02:06
  • 1
    @YoYoYonnY: I have no idea what currying has to do with this, but prototype methods don't leak variables or have changeable scope either. They do have a late-bound `this` context, but that's a feature not a problem. – Bergi Apr 11 '16 at 02:54
  • @Bergi you need `this` to access variables with `prototype` methods, it's not really an option. The only way to avoid this is with _closures_ (not currying, oops), which has the additional benefit of private variables. This nearly makes the prototype obsolete, since closures remove most performance gained by using prototypes in the first place. – yyny Apr 11 '16 at 12:02
  • @YoYoYonnY: So you mean properties, not variables? – Bergi Apr 11 '16 at 13:44
  • @Bergi OK, OK, I never program in JS anymore, call it whatever you want :P – yyny Apr 11 '16 at 22:50
  • "this.keys.remove is not a function", arrays doesn't have such function, you have to create it also: Array.prototype.remove = function(o) { var index = this.indexOf(o); if (index > -1) { this.splice(index, 1); return true; } return false; }; – Goty Metal Sep 29 '16 at 21:24
23

This is an old question, but because the existing answers could be very dangerous, I wanted to leave this answer for future folks who might stumble in here...

The answers based on using an Object as a HashMap are broken and can cause extremely nasty consequences if you use anything other than a String as the key. The problem is that Object properties are coerced to Strings using the .toString method. This can lead to the following nastiness:

function MyObject(name) {
  this.name = name;
};
var key1 = new MyObject("one");
var key2 = new MyObject("two");

var map = {};
map[key1] = 1;
map[key2] = 2;

If you were expecting that Object would behave in the same way as a Java Map here, you would be rather miffed to discover that map only contains one entry with the String key [object Object]:

> JSON.stringify(map);
{"[object Object]": 2}

This is clearly not a replacement for Java's HashMap. Bizarrely, given it's age, Javascript does not currently have a general purpose map object. There is hope on the horizon, though: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map although a glance at the Browser Compatability table there will show that this isn't ready to used in general purpose web apps yet.

In the meantime, the best you can do is:

  • Deliberately use Strings as keys. I.e. use explicit strings as keys rather than relying on the implicit .toString-ing of the keys you use.
  • Ensure that the objects you are using as keys have a well-defined .toString() method that suits your understanding of uniqueness for these objects.
  • If you cannot/don't want to change the .toString of the key Objects, when storing and retrieving the entries, convert the objects to a string which represents your understanding of uniqueness. E.g. map[toUniqueString(key1)] = 1

Sometimes, though, that is not possible. If you want to map data based on, for example File objects, there is no reliable way to do this because the attributes that the File object exposes are not enough to ensure its uniqueness. (You may have two File objects that represent different files on disk, but there is no way to distinguish between them in JS in the browser). In these cases, unfortunately, all that you can do is refactor your code to eliminate the need for storing these in a may; perhaps, by using an array instead and referencing them exclusively by index.

Rich
  • 997
  • 9
  • 12
18
var map = {'myKey1':myObj1, 'mykey2':myObj2};
// You don't need any get function, just use
map['mykey1']
wmnitin
  • 3,387
  • 1
  • 13
  • 19
David
  • 4,080
  • 1
  • 26
  • 34
6

If you're not restricted to JQuery, you can use the prototype.js framework. It has a class called Hash: You can even use JQuery & prototype.js together. Just type jQuery.noConflict();

var h = new Hash();
h.set("key", "value");
h.get("key");
h.keys(); // returns an array of keys
h.values(); // returns an array of values
polydor
  • 127
  • 1
  • 6