Anyone ever need to hash by object reference in javascript?
Maybe you want to group the children of uls by a hash of their DOM nodes, or attributes by instances of constructed objects, or many other things.
I have read this and don't want to use Weakmaps until they're stable.
The only way I can think to do it is storing parents in an array and enforcing reference uniqueness there, then hashing by the index of the reference in the array. (example is finding all the common UL parents of LIs):
var lis = document.getElementsByTagName('li'); // Get all objects
var parent, key, li;
for (var i=0; i<lis.length; i++ ) { // For every LI
storeItemByParent(lis[i], lis[i].parentNode);
}
var parents = [];
var itemsByParent = {};
function storeItemByParent (item, parent){
var key;
// Does this parent already exist? if so, use that index as the hash key
for (var j=0; j<parents.length; j++) {
if(parents[j] === parent) {
key = j;
break;
}
}
// This is a new parent? If so, track it and use that index as the hash key
if (key == undefined) {
parents.push(parent);
key = parents.length;
}
// Finally! The lis in a hash by parent "id".
itemsByParent[key] = itemsByParent[key] || [];
itemsByParent[key].push(item);
}
Is there a better way to store attributes or children or other things attached to an object instance without adding them as properties of the object itself?
1 Answer 1
You've pretty much got the right approach there, though I'd wrap it up nicely behind an interface, complete with my own add and find methods.
You can simply your coding somewhat by using <array>.indexOf, supported in all modern browsers and easily polyfilled when needed.
There is a downside to your approach however:
Should an element by removed from the DOM, that element won't be garbage collected because your array is still holding onto a reference to it.
While this isn't a show stopper, of course, it is worth keeping in mind.
There is, however, a totally different approach that you can take as well. May not be better -- but different.
Please forgive any minor errors in the code, I am typing this freehand:
function elementDataHash = {};
function setElementData(el, data) {
var hash = el.getAttribute("data-element-hash");
if !(hash) {
// http://stackoverflow.com/questions/6860853/generate-random-string-for-div-id
hash = generateRandomString();
el.setAttribute("data-element-hash", hash);
}
elementDataHash[hash] = data;
}
function getElementData(el) {
var hash = el.getAttribute("data-element-hash");
return elementDataHash[hash];
}
3 Comments
indexOf and garbage collection. And yes, totally should wrap in an interface, as this answer shows. Is "Dictionary" the correct CS term to use for this? I like the alternative approach too: attribute on the element stores a random hash, hash is used to get data. Similar GC issue though, right? Any time an el is removed, have to delete that entry from the hash table.elementDataHash and see if any of those elements survive in the DOM and, if not, clean up that entry. As for the word Dictionary -- I don't use that term for regular JavaScript objects as it will imply things from people coming from other backgrounds that aren't true. (In C#, for instance, a dictionary can have any type of key, it doesn't have to be a string.)el.setAttribute("data-element-hash", hash);
.data()method pretty much implements what you want..data()is pretty much the idea :)