What is deterministic-object-hash?
The deterministic-object-hash npm package is used to create consistent and deterministic hashes for JavaScript objects. This is particularly useful for scenarios where you need to ensure that the same object always produces the same hash, regardless of the order of its properties.
What are deterministic-object-hash's main functionalities?
Basic Hashing
This feature allows you to generate a deterministic hash for a given object. The order of properties in the object does not affect the hash value.
const deterministicHash = require('deterministic-object-hash');
const obj = { b: 2, a: 1 };
const hash = deterministicHash(obj);
console.log(hash);
Custom Hashing Algorithm
This feature allows you to specify a custom hashing algorithm. In this example, the 'sha256' algorithm is used to generate the hash.
const deterministicHash = require('deterministic-object-hash');
const obj = { b: 2, a: 1 };
const options = { algorithm: 'sha256' };
const hash = deterministicHash(obj, options);
console.log(hash);
Hashing with Custom Sort Function
This feature allows you to provide a custom sort function to determine the order of properties before hashing. This can be useful for more complex sorting requirements.
const deterministicHash = require('deterministic-object-hash');
const obj = { b: 2, a: 1 };
const options = { sort: (a, b) => a.localeCompare(b) };
const hash = deterministicHash(obj, options);
console.log(hash);
Other packages similar to deterministic-object-hash
object-hash
The object-hash package generates hashes for JavaScript objects. It offers various hashing algorithms and options for customization. Compared to deterministic-object-hash, object-hash provides more flexibility in terms of hashing algorithms and options but may not guarantee the same level of determinism in all cases.
json-stable-stringify
The json-stable-stringify package is used to stringify JavaScript objects in a deterministic way by sorting the properties. This can be used in conjunction with a hashing function to achieve similar results to deterministic-object-hash. However, it requires an additional step to hash the stringified output.
crypto
The built-in crypto module in Node.js can be used to create hashes for strings. When combined with a stable stringification method like json-stable-stringify, it can achieve similar functionality to deterministic-object-hash. However, it requires more manual setup and does not provide a single, cohesive API for deterministic object hashing.
Deterministic-Object-Hash
A deterministic object hashing algorithm for Node.js and web platform.
The Problem
Using JSON.stringify
on two objects that are deeply equal does not lead to the same output string. Instead the keys are ordered in the same order that they were added. This leads to two objects that are deeply equal being hashed to different values.
import { createHash } from "crypto";
import { isEqual } from "lodash";
const obj1: Record<string, string> = {};
obj1['a'] = 'x';
obj1['b'] = 'y';
obj1['c'] = 'z';
const obj2: Record<string, string> = {};
obj2['c'] = 'z';
obj2['b'] = 'y';
obj2['a'] = 'x';
isEqual(obj1, obj2);
const string1 = JSON.stringify(obj1);
const string2 = JSON.stringify(obj2);
createHash('sha1').update(string1).digest('hex');
createHash('sha1').update(string2).digest('hex');
Usage
Pass a value and receive a deterministic hash of the value.
import deterministicHash from 'deterministic-object-hash';
const objA = { a: 'x', arr: [1,2,3,4], b: 'y' };
const objB = { b: 'y', a: 'x', arr: [1,2,3,4] };
await deterministicHash({
c: [ objA, objB ],
b: objA,
e: objB,
f: ()=>{ Math.random(); },
g: Symbol('Unique identity'),
h: new Error('AHHH')
});
await deterministicHash({
h: new Error('AHHH'),
e: objB,
g: Symbol('Unique identity'),
b: objA,
f: ()=>{ Math.random(); },
c: [ objA, objB ]
});
Settings
A hash algorithm can be passed as the second argument. This takes any value that is valid for webcrypto.subtle.digest
. The default is SHA-1
.
A digest format can be passed as the third argument. This takes any value that is valid for Hash.digest
. The default is hex
.
await deterministicHash('value', 'SHA-1');
await deterministicHash('value', 'SHA-256', 'hex');
await deterministicHash('value', 'SHA-512', 'base64');
Supported Values
| |
---|
String | Number |
Boolean | Function |
Plain Objects | Symbol |
undefined | null |
Infinity | NaN |
BigInt | Array |
Classes/Inheritance | Errors |
Date | RegExp |
Map | Set |
Int8Array | Uint8Array |
Int16Array | Uint16Array |
Int32Array | Uint32Array |
Float32Array | Float64Array |
BigInt64Array | BigUint64Array |
Uint8ClampedArray | globalThis |
ArrayBuffer | SharedArrayBuffer |
Unsupported Values
| |
---|
WeakMap | WeakSet |
Atomics | DataView |
Promises | Reflect |
Proxy | |
Due to their nature, WeakSet and WeakMap are not enumerable. As a result there is no way to know what is in a WeakSet/WeakMap unless we are told.
Support
Currently this has only been tested on Node.js 18.x.x
. More tests are to come and this section will be updated as I test them.
Contributors
Contributions are welcome. Feel free to create a PR and tag zbauman3.
Past contributors: