lib/util/collection/HashMap.js
var forEach = require('lodash.foreach');
var Class = require('../../ext/Class');
var ObjectUtils = require('./../ObjectUtils');
var HashMap = Class.create({
initialize: function(fnEquals, fnHash) {
this.fnEquals = fnEquals ? fnEquals : ObjectUtils.isEqual;
this.fnHash = fnHash ? fnHash : ObjectUtils.defaultHashCode;
this.hashToBucket = {};
var self = this;
this.fnGet = function(key) {
var r = self.get(key);
return r;
};
// NOTE: Defining a .length property will cause lodash.forEach and probably some other
// JS stuff out there mistake this object for an array and cause unexpected behavior.
// For this reason, use .size()
// Object.defineProperty(this, 'length', {
// get: function() {
// var r = self.entries().length;
// return r;
// }
// });
},
hashCode: function() {
var self = this;
// The hash system is messy - need to clean up
var hashCodeWrapper = function(val) {
var r = self.fnHash(val);
if(r == null) {
r = 3;
}
else if(ObjectUtils.isString(r)) {
r = ObjectUtils.hashCodeStr(r);
}
return r;
};
if(this.hash == null) {
var entries = this.entries();
var h = 0;
entries.forEach(function(entry) {
var keyHash = hashCodeWrapper(entry.key);
var valHash = hashCodeWrapper(entry.val);
h += 19 * keyHash + 7919 * valHash;
});
this.hash = h;
}
return this.hash;
},
equals: function(that) {
throw new Error('Not implemented yet');
},
clear: function() {
this.hashToBucket = {};
this.hash = null;
},
putEntry: function(entry) {
this.put(entry.key, entry.val);
},
putEntries: function(entries) {
var self = this;
entries.forEach(function(entry) {
self.putEntry(entry);
});
return this;
},
// Deprecated - use putMap instead
putAll: function(map) {
return this.putMap(map);
},
putMap: function(map) {
var entries = map.entries();
var result = this.putEntries(entries);
return result;
},
getOrCreate: function(key, initVal) {
var result;
var containsKey = this.containsKey(key);
if(!containsKey) {
this.put(key, initVal);
result = initVal;
} else {
result = this.get(key);
}
return result;
},
put: function(key, val) {
this.hash = null;
var hash = this.fnHash(key);
var bucket = this.hashToBucket[hash];
if (bucket == null) {
bucket = [];
this.hashToBucket[hash] = bucket;
}
var keyIndex = this._indexOfKey(bucket, key);
if (keyIndex >= 0) {
bucket[keyIndex].val = val;
return;
}
var entry = {
key: key,
val: val,
};
bucket.push(entry);
},
_indexOfKey: function(bucket, key) {
if (bucket != null) {
for (var i = 0; i < bucket.length; ++i) {
var entry = bucket[i];
var k = entry.key;
if (this.fnEquals(k, key)) {
// entry.val = val;
return i;
}
}
}
return -1;
},
get: function(key) {
var hash = this.fnHash(key);
var bucket = this.hashToBucket[hash];
var i = this._indexOfKey(bucket, key);
var result = i >= 0 ? bucket[i].val : null;
return result;
},
remove: function(key) {
var hash = this.fnHash(key);
var bucket = this.hashToBucket[hash];
var i = this._indexOfKey(bucket, key);
var doRemove = i >= 0;
if (doRemove) {
this.hash = null;
bucket.splice(i, 1);
if(bucket.length === 0) {
delete this.hashToBucket[hash];
}
}
return doRemove;
},
containsKey: function(key) {
var hash = this.fnHash(key);
var bucket = this.hashToBucket[hash];
var result = this._indexOfKey(bucket, key) >= 0;
return result;
},
keys: function() {
var result = [];
forEach(this.hashToBucket, function(bucket) {
var keys = [];
bucket.forEach(function(item) {
if (item.key) {
keys.push(item.key);
}
});
result.push.apply(result, keys);
});
return result;
},
values: function() {
var entries = this.entries();
var result = entries.map(function(entry) {
return entry.val;
});
return result;
},
entries: function() {
var result = [];
forEach(this.hashToBucket, function(bucket) {
result.push.apply(result, bucket);
});
return result;
},
// Convenience function to iterate all pairs in this map
// with map.forEach(function(v, k) { });
forEach: function(callback) {
var entries = this.entries();
entries.forEach(function(entry) {
callback(entry.val, entry.key);
});
},
toString: function() {
var entries = this.entries();
var entryStrs = entries.map(function(entry) {
return entry.key + ': ' + entry.val;
});
var result = '{' + entryStrs.join(', ') + '}';
return result;
},
/**
* Returns a function for getting elements
*/
asFn: function() {
return this.fnGet;
},
size: function() {
var entries = this.entries();
var result = entries.length;
return result;
},
isEmpty: function() {
var l = this.size();
var result = l === 0;
return result;
}
});
module.exports = HashMap;