Skip to content

Instantly share code, notes, and snippets.

@jed
Created January 4, 2011 17:21
Show Gist options
  • Save jed/765059 to your computer and use it in GitHub Desktop.
Save jed/765059 to your computer and use it in GitHub Desktop.
objective: a function for generating guids of unique entities
// objective: a function for generating guids of unique entities
// usage: guid( obj ) => integer
// approach 1
// entities kept in a hash array, key id found by iteration
// pros:
// (1) unobtrusive; doesn't pollute objects with expandos
// (2) ids can't be accidentally deleted/changed
// (3) ids can be assigned to any type of object
// (4) id => entity lookup could be implemented for free
// cons:
// (1) id lookup is O(N), a pretty big deal
// (2) GC memory leaked unless "unset" is implemented
guid1 = function( guid, objs, i ) {
return function( obj ) {
for ( i = guid; i--; ) if ( objs[ i ] === obj ) return i
return objs[ guid ] = obj, guid++
}
}( 1, [] )
// approach 2
// ids kept on each object as a property, a la jQuery
// pros:
// (1) id lookup is O(1)
// (2) no memory leaks, since only primitive integers are stored
// cons:
// (1) ids can be erased, may interfere with iteration
// (2) hacky expando string required
guid2 = function( guid, expando ) {
return function( obj ) {
if ( obj ) {
if ( obj[ expando ] ) return obj[ expando ]
obj[ expando ] = guid
if ( obj[ expando ] ) return guid++
}
}
}( 1, Date.now().toString( 36 ) )
@tlrobinson
Copy link

Ooops noticed I'm missing a "return" on the last line of approach 1 example: "bucket.guids[index];"

@jed
Copy link
Author

jed commented Jan 4, 2011

yeah, i added it for ya.

i'm not to keen on relying on defineProperty or indexOf, so i'm thinking seriously about a variation on your guid1:

guid1 = function( guid, buckets, bucket, i, l, objs, guids ) {
  return function( obj ) {
    bucket = buckets[ obj ] || ( buckets[ obj ] = { objs: [], guids: [] } )
    objs = bucket.objs
    guids = bucket.guids
    i = l = objs.length

    while ( i-- ) if ( objs[ i ] === obj ) return guids[ i ]

    return objs[ l ] = obj, guids[ l ] = guid++
  }
}( 1, {} )

i like this a lot, but worry about how/when to invalidate the cache. also, id => obj is no longer O(1)...

@jed
Copy link
Author

jed commented Jan 4, 2011

and to remove some array overhead, and perform lookup by constructor name:

guid1 = function( guid, buckets, bucket, name, i, l ) {
  return function( obj ) {
    name = obj ? obj.constructor.name : obj
    bucket = buckets[ name ] || ( buckets[ name ] = [] )
    i = l = bucket.length

    while ( i-- ) if ( bucket[ i-- ] === obj ) return bucket[ i ]

    return bucket[ l + 1 ] = obj, bucket[ l ] = guid++
  }
}( 1, {} )

@jed
Copy link
Author

jed commented Jan 5, 2011

or if you're okay with unique strings in the style of constructor-name/id:

function guid( obj ) {
  for (
    var type = obj ? obj.constructor.name : obj
      , list = guid[ type ] || ( guid[ type ] = [] )
      , i = list.length
      ; i-- && list[ i ] !== obj;
  );

  return ++i || ( i = list.push( obj ) ), type + "/" + i
}

@tlrobinson
Copy link

indexOf is significantly faster than manually iterating. I'd suggest checking for it's existence and falling back to manual iteration.

@tlrobinson
Copy link

Also, you would win a JavaScript obfuscation contest. It took me a solid minute to figure out how that 7 line function worked.

@jed
Copy link
Author

jed commented Jan 7, 2011

i dunno, is indexOf really that much faster?

and yeah, my style is a bit, uh, terse i guess...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment