-
-
Save alaa-eddine/6317515 to your computer and use it in GitHub Desktop.
//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); |
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.
Maybe you could add some generics 😁
You should avoid multiple calls to
this.index[key]
in order to improve performances.Replace:
by:
index
will be bigger but deletion with perform faster!