Skip to content

Instantly share code, notes, and snippets.

@alaa-eddine
Last active April 23, 2019 07:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save alaa-eddine/6317515 to your computer and use it in GitHub Desktop.
Save alaa-eddine/6317515 to your computer and use it in GitHub Desktop.
This is an experimental HashMap implementation with some features I needed and didn't found in JS default Arrays and Objects Features : * Direct acces too elements throught .get(key) * fast keys or values iteration using for (;;) instead of for in syntax (http://jsperf.com/array-keys-vs-object-keys-iteration/3 )
//this is the compiled js version
var HashMap = (function () {
function HashMap() {
this.length = 0;
this.values = [];
this.index = {
};
this.keys = [];
}
HashMap.prototype.add = function (key, value) {
if(key === undefined && value === undefined) {
return undefined;
}
var previous = undefined;
//Are we replacing an existing element ?
if(this.hasKey(key)) {
previous = this.values[this.index[key].data];
this.values[this.index[key].data] = undefined;
this.keys[this.index[key].key] = undefined;
} else {
this.length++;
}
//create a new index, this will replace existing one.
this.index[key] = {
data: this.values.push(value) - 1,
key: this.keys.push(key) - 1
};
return previous;
};
HashMap.prototype.get = function (key) {
if(this.hasKey(key)) {
return this.values[this.index[key].data];
}
};
HashMap.prototype.hasKey = function (key) {
return (this.index[key] !== undefined);
};
HashMap.prototype.remove = function (key) {
if(this.hasKey(key)) {
var previous = this.values[this.index[key].data];
this.values[this.index[key].data] = undefined;
this.keys[this.index[key].key] = undefined;
this.length--;
delete this.index[key];
return previous;
} else {
return undefined;
}
//TODO : clean this.keys and this.values from undefined values when their size becomes too big
};
HashMap.prototype.each = //helper function to iterate throught all values
function (fn) {
if(typeof fn != 'function') {
return;
}
for(var i = 0; i < this.values.length; i++) {
fn(this.values[i]);
}
};
HashMap.prototype.clear = function () {
this.values.length = 0;
this.index = {
};
this.keys.length = 0;
this.length = 0;
};
return HashMap;
})();
/*
This is an experimental HashMap implementation with some features I needed and didn't found in JS default Arrays and Objects
Features :
* Direct acces too elements throught .get(key)
* fast keys or values iteration using for (;;) instead of for in syntax (http://jsperf.com/array-keys-vs-object-keys-iteration/3 )
*/
class HashMap {
public length: number = 0;
//Values will be stored here giving fast iteration with a fro (;;) loop
public values: any[];
//Keys will be stored here giving fast iteration throught keys with a fro (;;) loop
public keys: any[];
//this is a map between keys and values to give fast direct access to an element knowing its key
public index: any;
constructor() {
this.values = [];
this.index = {};
this.keys = [];
}
add(key, value) {
if (key === undefined && value === undefined) return undefined;
var previous = undefined;
//Are we replacing an existing element ?
if (this.hasKey(key)) {
previous = this.values[this.index[key].data];
this.values[this.index[key].data] = undefined;
this.keys[this.index[key].key] = undefined;
}
else {
this.length++;
}
//create a new index, this will replace existing one.
this.index[key] = {
data: this.values.push(value) - 1,
key: this.keys.push(key) - 1
};
return previous;
}
get (key) {
if (this.hasKey(key))
return this.values[this.index[key].data];
}
hasKey(key) {
return (this.index[key] !== undefined);
}
remove(key) {
if (this.hasKey(key)) {
var previous = this.values[this.index[key].data];
this.values[this.index[key].data] = undefined;
this.keys[this.index[key].key] = undefined;
this.length--;
delete this.index[key];
return previous;
}
else {
return undefined;
}
//TODO : clean this.keys and this.values from undefined values when their size becomes too big
}
//helper function to iterate throught all values
each(fn) {
if (typeof fn != 'function') return;
for (var i = 0; i < this.values.length; i++) {
fn(this.values[i]);
}
}
clear() {
this.values.length = 0;
this.index = {};
this.keys.length = 0;
this.length = 0;
}
}
var hashmap = new HashMap();
hashmap.add('some key', {id:0, someparam:'hello'});
hashmap.add('key01', 55);
for (var i = 0; i< 10; i++)
hashmap.add(i, {id:'obj'+i});
hashmap.each(function(value){
console.log(value);
})
hashmap.remmove(15);
hashmap.remmove('key01');
console.log(hashmap.keys);
console.log(hashmap.values);
console.log(hashmap.indexe);
@ooflorent
Copy link

Maybe you could add some generics 😁
You should avoid multiple calls to this.index[key] in order to improve performances.

Replace:

delete this.index[key];

by:

this.index[key] = undefined;

index will be bigger but deletion with perform faster!

@rbrundritt
Copy link

I like the idea. I've done some extensive testing of this in multiple browsers. My test consisted of creating a data size of 250,000 and iterating over it once.

I found that the add function seems to be the main bottle next in terms of overall performance, which is to be expected. However, as-is, this bottle neck results in the class being slower than using an associative array in my test. Going through and ripping out some of the safety checks and the option to return the previous value if overwriting a key, the performance of the add function had a notable improvement and ended up being faster than associative arrays in Chrome and Edge by 60 to 100 ms. However, in all other browsers the difference in performance was minimal.

All that said, where this class really shines is when you need to iterate over the data more than once. If your application requires you to loop through a key index data set more than once, then this would be the fastest solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment