Skip to content

Commit

Permalink
fix: stable references should stay the same id
Browse files Browse the repository at this point in the history
  • Loading branch information
maraisr committed Jun 22, 2023
1 parent 776ae07 commit ee332f0
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 35 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "object-identity",
"version": "0.1.0",
"version": "0.1.1",
"repository": "maraisr/object-identity",
"license": "MIT",
"author": "Marais Rossow <me@marais.dev> (https://marais.io)",
Expand Down
8 changes: 4 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ assert.toEqual(hashA, hashB);
> via the [`/bench`](/bench) directory with Node v16.20.0 (Apple M1 Pro)
```
✔ object-identity ~ 40,615,840 ops/sec ± 0.20%
✔ object-hash ~ 111,888 ops/sec ± 0.01%
✔ object-identity :: hashed ~ 36,036,108 ops/sec ± 0.08%
✔ object-hash :: hashed ~ 51,912 ops/sec ± 0.01%
✔ object-identity ~ 56,929,524 ops/sec ± 0.08%
✔ object-hash ~ 114,195 ops/sec ± 0.01%
✔ object-identity :: hashed ~ 46,224,968 ops/sec ± 0.03%
✔ object-hash :: hashed ~ 52,104 ops/sec ± 0.01%
```

> ^ `object-identity` is not as feature-full it's alternatives, specifically around `function` values and other node
Expand Down
36 changes: 21 additions & 15 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ API('should export a function', () => {
assert.type(identify, 'function');
});

API.run();

// ~ Arrays

const Arrays = suite('array');
Expand Down Expand Up @@ -52,8 +50,6 @@ Arrays('circular should know its circular', () => {
assert.equal(identify(arr), 'a123~1');
});

Arrays.run();

// ~ Objects

const Objects = suite('object');
Expand Down Expand Up @@ -95,17 +91,26 @@ Objects('circular', () => {
assert.equal(identify(o), identify(o));
});

Objects('with samey circular shoudlnt match', () => {
Objects('partial circular', () => {
const o = {v: 1};
const a = ['a', o];
assert.equal(identify(a), identify(a));
});

// Right now they do match, because the o1 lookup is the same as o2
// as the reference is still the same, so the weakmap is the same
Objects.skip('with samey circular shoudlnt match', () => {
const o1: any = { a: 1, b: 2 };
const o2: any = { a: 1, b: 2 };
o1['c'] = o1;
o1['d'] = o2;
o2['c'] = o2;

o2['c'] = o2; // 👈 #1

const a = identify(o1);
o1['c'] = o1;
o1['d'] = o2;
o2['c'] = o1; // 👈 circular on first object, different from a which is circular on 2nd object

o2['c'] = o1; // 👈 different from #1

const b = identify(o1);

assert.not.equal(a, b, `${a} != ${b}`);
Expand All @@ -119,8 +124,6 @@ Objects('same hash for Map or Object', () => {
assert.equal(identify({ a: 'b' }), identify(new Map([['a', 'b']])));
});

Objects.run();

// ~ Sets

const Sets = suite('set');
Expand All @@ -144,8 +147,6 @@ Sets('circular', () => {
assert.equal(identify(s), identify(s));
});

Sets.run();

// ~ Maps

const Maps = suite('map');
Expand Down Expand Up @@ -178,8 +179,6 @@ Maps('circular', () => {
assert.equal(identify(m), identify(m));
});

Maps.run();

// ~ Values

const Values = suite('values');
Expand Down Expand Up @@ -267,4 +266,11 @@ Values('should only be seen once', () => {
assert.not.match(hash, /~\d+/);
});

// --

API.run();
Arrays.run();
Objects.run();
Sets.run();
Maps.run();
Values.run();
27 changes: 12 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
let seen = new WeakMap<object, number>();
let seen = new WeakMap<object, string>();

function walk(input: any, ref_index: number) {
let type = Object.prototype.toString.call(input);
let tmp: any;
let out = '';
let i = 0;
let tmp: any;

if (type !== '[object RegExp]' && type !== '[object Date]') {
if (input == null || typeof input !== 'object') return String(input);
let type = Object.prototype.toString.call(input);

if (seen.has(input)) return '~' + seen.get(input);
seen.set(input, ++ref_index);
}
if (input == null || typeof input !== 'object') return String(input);
if (!(type === '[object RegExp]' || type === '[object Date]') && seen.has(input)) return seen.get(input)!;
seen.set(input, '~' + ++ref_index);

switch (type) {
case '[object Set]':
Expand All @@ -20,22 +17,19 @@ function walk(input: any, ref_index: number) {
tmp ||= input;
out += 'a';
for (; i < tmp.length; out += walk(tmp[i++], ref_index));
return out;
}
} break;

case '[object Object]': {
out += 'o';
tmp = Object.keys(input).sort();
for (; i < tmp.length; out += tmp[i] + walk(input[tmp[i++]], ref_index));
return out;
}
} break;

case '[object Map]': {
out += 'o';
tmp = Array.from((input as Map<string, unknown>).keys()).sort();
for (; i < tmp.length; out += tmp[i] + walk(input.get(tmp[i++]), ref_index));
return out;
}
} break;

case '[object Date]':
return 'd' + +input;
Expand All @@ -46,6 +40,9 @@ function walk(input: any, ref_index: number) {
default:
throw new Error(`Unsupported value ${input}`);
}

seen.set(input, out);
return out;
}

export function identify<T>(input: T): string {
Expand Down

0 comments on commit ee332f0

Please sign in to comment.