Basically, I\'m trying to create an object of unique objects, a set. I had the brilliant idea of just using a JavaScript object with objects for the property names. Such as,
I combined the answers from eyelidlessness and KimKha.
The following is an angularjs service and it supports numbers, strings, and objects.
exports.Hash = () => {
let hashFunc;
function stringHash(string, noType) {
let hashString = string;
if (!noType) {
hashString = `string${string}`;
}
var hash = 0;
for (var i = 0; i < hashString.length; i++) {
var character = hashString.charCodeAt(i);
hash = ((hash<<5)-hash)+character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
function objectHash(obj, exclude) {
if (exclude.indexOf(obj) > -1) {
return undefined;
}
let hash = '';
const keys = Object.keys(obj).sort();
for (let index = 0; index < keys.length; index += 1) {
const key = keys[index];
const keyHash = hashFunc(key);
const attrHash = hashFunc(obj[key], exclude);
exclude.push(obj[key]);
hash += stringHash(`object${keyHash}${attrHash}`, true);
}
return stringHash(hash, true);
}
function Hash(unkType, exclude) {
let ex = exclude;
if (ex === undefined) {
ex = [];
}
if (!isNaN(unkType) && typeof unkType !== 'string') {
return unkType;
}
switch (typeof unkType) {
case 'object':
return objectHash(unkType, ex);
default:
return stringHash(String(unkType));
}
}
hashFunc = Hash;
return Hash;
};
Example Usage:
Hash('hello world'), Hash('hello world') == Hash('hello world')
Hash({hello: 'hello world'}), Hash({hello: 'hello world'}) == Hash({hello: 'hello world'})
Hash({hello: 'hello world', goodbye: 'adios amigos'}), Hash({hello: 'hello world', goodbye: 'adios amigos'}) == Hash({goodbye: 'adios amigos', hello: 'hello world'})
Hash(['hello world']), Hash(['hello world']) == Hash(['hello world'])
Hash(1), Hash(1) == Hash(1)
Hash('1'), Hash('1') == Hash('1')
Output
432700947 true
-411117486 true
1725787021 true
-1585332251 true
1 true
-1881759168 true
Explanation
As you can see the heart of the service is the hash function created by KimKha.I have added types to the strings so that the sturucture of the object would also impact the final hash value.The keys are hashed to prevent array|object collisions.
eyelidlessness object comparision is used to prevent infinit recursion by self referencing objects.
Usage
I created this service so that I could have an error service that is accessed with objects. So that one service can register an error with a given object and another can determine if any errors were found.
ie
JsonValidation.js
ErrorSvc({id: 1, json: '{attr: "not-valid"}'}, 'Invalid Json Syntax - key not double quoted');
UserOfData.js
ErrorSvc({id: 1, json: '{attr: "not-valid"}'});
This would return:
['Invalid Json Syntax - key not double quoted']
While
ErrorSvc({id: 1, json: '{"attr": "not-valid"}'});
This would return
[]
If you want a hashCode() function like Java's in JavaScript, that is yours:
String.prototype.hashCode = function(){
var hash = 0;
for (var i = 0; i < this.length; i++) {
var character = this.charCodeAt(i);
hash = ((hash<<5)-hash)+character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
That is the way of implementation in Java (bitwise operator).
Please note that hashCode could be positive and negative, and that's normal, see HashCode giving negative values. So, you could consider to use Math.abs()
along with this function.
In ECMAScript 6 there's now a Set
that works how you'd like: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
It's already available in the latest Chrome, FF, and IE11.
For my specific situation I only care about the equality of the object as far as keys and primitive values go. The solution that worked for me was converting the object to its JSON representation and using that as the hash. There are limitations such as order of key definition potentially being inconsistent; but like I said it worked for me because these objects were all being generated in one place.
var hashtable = {};
var myObject = {a:0,b:1,c:2};
var hash = JSON.stringify(myObject);
// '{"a":0,"b":1,"c":2}'
hashtable[hash] = myObject;
// {
// '{"a":0,"b":1,"c":2}': myObject
// }
In addition to eyelidlessness's answer, here is a function that returns a reproducible, unique ID for any object:
var uniqueIdList = [];
function getConstantUniqueIdFor(element) {
// HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
if (uniqueIdList.indexOf(element) < 0) {
uniqueIdList.push(element);
}
return uniqueIdList.indexOf(element);
}
As you can see it uses a list for look-up which is very inefficient, however that's the best I could find for now.
I put together a small JavaScript module a while ago to produce hashcodes for strings, objects, arrays, etc. (I just committed it to GitHub :) )
Usage:
Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159