2

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?

Community
  • 1
  • 1
SimplGy
  • 20,079
  • 15
  • 107
  • 144

1 Answers1

2

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];
}
Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
  • Good note on the `indexOf` and garbage collection. And yes, totally should wrap in an interface, as [this answer](http://stackoverflow.com/a/21342955/111243) 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. – SimplGy Aug 19 '14 at 16:46
  • @SimpleAsCouldBe Depends on the size of the data stored. With the second approach, one could walk through the `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`.) – Jeremy J Starcher Aug 19 '14 at 16:50
  • @SimpleAsCouldBe Head's up -- I fixed the line `el.setAttribute("data-element-hash", hash);` – Jeremy J Starcher Aug 19 '14 at 16:54