deadbeef
v1.1.3
Published
deadbeef is a unique key generator that will generate a key for any combo of anything
Downloads
10
Maintainers
Readme
deadbeef
deadbeef
is a unique key generator that will generate a key for any combo of anything--without leaking memory.
deadbeef
has some design constraints due to how Javascript engines work (to not to leak memory), and so the unique keys generated won't always be the same length, might be very long, and aren't very good to use as an id that can be persisted. The keys generated will also differ across application runs, and engine contexts (i.e. web-workers). However, given a single running context, the keys are guaranteed to be unique and consistent.
The reason I created deadbeef
was to be able to create keys for caching engines. Any use-case that requires consistent unique keys within a single engine context is a perfect fit for deadbeef
. If you require unique consistency across machines or different engine contexts, then you are out of luck! Sorry!
Install
npm i --save deadbeef
Use
To use deadbeef
, simply throw any number of arguments at it to get a unique key. Keys generated by deadbeef
will always be strings. Keys are guaranteed to be unique and consistent for the arguments provided.
For example:
const deadbeef = require('deadbeef');
console.log(deadbeef('hello', 'world'));
// -> '2:string:hello:string:world'
console.log(deadbeef('hello', 'world'));
// -> '2:string:hello:string:world'
console.log(deadbeef('world', 'hello'));
// -> '2:string:world:string:hello'
However, deadbeef
is designed such that we shouldn't care what the generated value is. Rather, the importance is in the equality of unique values generated. These can for example be used as keys into a Map
.
const deadbeef = require('deadbeef');
console.log(deadbeef('hello', 'world') === deadbeef('hello', 'world'));
// -> true
console.log(deadbeef('hello', 'world') === deadbeef('hello', 'world1'));
// -> false
And the coolest part is that deadbeef
will accept absolutely any value to create a key from, even null
, undefined
, functions, objects, symbols, and no arguments at all!
assert.strictEqual(deadbeef(), deadbeef());
assert.strictEqual(deadbeef(undefined), deadbeef(undefined));
assert.strictEqual(deadbeef(null), deadbeef(null));
assert.strictEqual(deadbeef(1), deadbeef(1));
assert.strictEqual(deadbeef(1, 1), deadbeef(1, 1));
assert.strictEqual(deadbeef(1, 1, true), deadbeef(1, 1, true));
const myFunction = () => {};
assert.strictEqual(deadbeef(myFunction, true), deadbeef(myFunction, true));
Sorted
deadbeef
has another method that can be used to generate a unique key: deadbeef.sorted
. This sorts the generated ids for each argument before combining them. This means that you can check equality across many elements, even if they are not in order. For example, if you would like to know if two arrays contain the same values (even if not in the same order), you can do so:
const deadbeef = require('deadbeef');
assert.strictEqual(deadbeef.sorted(...[ 1, 2, 3, 4, 5 ]), deadbeef.sorted(...[ 5, 3, 2, 4, 1 ]));
As always, this works with all values, not just numbers or strings. Throw functions at it, objects, symbols, who cares!
Want to check if two objects are identical in their contents? i.e. to check for "prop differences"? No problamo pal!
let obj1 = { test: true, 1: 2, node: '.js' };
let obj2 = { 1: 2, node: '.js', test: true };
assert.strictEqual(
deadbeef.sorted(
...Object.keys(obj1),
...Object.values(obj1)
),
deadbeef.sorted(
...Object.keys(obj2),
...Object.values(obj2)
)
);
Maps
Since you can generate a unique id for any combo of values, you can easily use a Map
, or a standard Object
to store metadata on a combination of values, to use in caching engines, or whatever!
function getCachedValue(...values) {
let cacheKey = deadbeef(...values);
if (!myCacheMap.has(cacheKey));
return;
return myCacheMap.get(cacheKey);
}
function setCachedValue(value, ...values) {
let cacheKey = deadbeef(...values);
myCacheMap.set(cacheKey, value);
}
Generating your own ids
deadbeef
will look for a Symbol.for('@@deadbeefUniqueID')
symbol key on any value it is generating an id for. If this key exists on any given value, and is a function, then deadbeef
will call it, and generate a unique id on the return value (which could be another generated id).
deadbeef
has a helper property called deadbeef.idSym
that is this symbol.
For example, let's say you have User
models loaded from the database, which might be different references, but might have the same id
. You could add a Symbol.for('@@deadbeefUniqueID')
method to your user models, and return the user id
, which means deadbeef
would always generate the same unique key for your User
model instances, even though they might be different instances:
class User {
constructor(userID) {
this.id = userID;
}
[deadbeef.idSym]() {
return this.id;
}
}
// Two of the same user...
// but two different instances
let user1 = new User(123);
let user2 = new User(123);
console.log(deadbeef(user1) === deadbeef(user2));
// -> true
Or, maybe in your case users are unique when they have the same id
and organizationID
. No problamo gentleladies!
class User {
constructor(userID, organizationID) {
this.id = userID;
this.organizationID = organizationID;
}
[deadbeef.idSym]() {
// Generate a unique key that
// will generate a unique key!
return deadbeef(this.id, this.organizationID);
}
}
// Two of the same user...
// but two different instances
let user1 = new User(42, 0xDEADBEEF);
let user2 = new User(42, 0xDEADBEEF);
console.log(deadbeef(user1) === deadbeef(user2));
// -> true
Generating ids for foreign types
But what if I want to generate an id for a type I don't control? Let's say for example you want to generate ids in the browser for Element
types, where the id will be generated based on the elements tag name, and its id
attribute. No problem!
deadbeef
has the method generateIDFor
which will allow you to generate an id for any type of value. The way this works is that you provide generateIDFor
a helper
method that will check if the type matches, and a generator
method that will generate the id for a given type.
In our example, we want to generate an id for Element
types in the browser. Here is how you would do that:
const deadbeef = require('deadbeef');
const isElementHelper = (value) => value instanceof Element;
// Set us up to generate ids for Element types
deadbeef.generateIdFor(
isElementHelper,
(element) => deadbeef(element.tagName, element.getAttribute('id')),
);
// Are elements the same?
console.log(deadbeef(document.getElementByID('myDiv1')) === deadbeef(document.getElementByID('myDiv1')));
// Don't forget to cleanup!
// To do so, we pass the `helper`
// method to `removeIDGenerator`
deadbeef.removeIDGenerator(isElementHelper);
The sky is the limit
Though this library is super simple, it allows for some really awesome things to be created. Just think of the possibilities!
- Check if arrays contain all the same values
- Check if arrays contain all of the same values in the same order
- Check object equality
- Generate cache keys for anything, or any combo of anything
- Check if a UI component is the same, and has the same children
- Check if two instances of anything are the same
- Check if multiple instances are the same
- Generate combo-keys for Maps
- Check if Elements have the same classes, styles, attributes, or whatever!
- ... and much more!
Enjoy! Just for kix and giggles, drop us a line to let us know the creative application you have discovered for deadbeef
.
What's with the name bro?
Generating unique ids for anything in javascript is no easy task (if you care about memory leaks... which you should). I couldn't think up any better name, and 0xDEADBEEF
has a long history in software development in debugging and memory-related issues. Because of its history, and relation to memory, I figured it was a perfect name for the library.
If you don't like the name, feel free to import it under another name!
const ID = require('deadbeef');
if (ID(0xDEADBEEF, 'hello', 'moo moo') === ID(0xDEADBEEF, 'hello', 'moo moo'))
console.log('We ❤️️ cows! Moo!');
Requirements
The only requirement for deadbeef
to work properly on your javascript engine is the presence and proper implementation of WeakMap
. If the engine (or version of the engine) you are using supports WeakMap
, then deadbeef
will work for you. For NodeJS, this requires version 0.12.0
or later.