This proposal introduces Object.equiv
, Object.diff
, and Symbol.diff
.
Object.equiv
would essentially be:
function equiv (objectA, objectB) {
for (const d of Object.diff(objectA, objectB)) {
return false;
}
return true;
}
class Rectangle {
constructor (height, weight) {
this.height = height;
this.width = width;
}
* [Symbol.diff] (other) {
// Check for identical constructors.
if (!(other instanceof this.constructor)) {
return yield {
op: 'replace',
from: this,
value: other,
};
}
// Check for identical heights.
if (this.height !== other.height) {
return yield {
op: 'replace',
path: [ 'height' ],
from: this.height,
value: other.height,
};
}
// Check for identical widths.
if (this.width !== other.width) {
return yield {
op: 'replace',
path: [ 'width' ],
from: this.width,
value: other.width,
};
}
}
class ArrayLike {
constructor (length) {
this.length = length;
}
* [Symbol.diff] (other) {
// Check for identical constructors.
if (!(other instanceof this.constructor)) {
return yield {
op: 'replace',
from: this,
value: other,
};
}
// The overlap between this and the other ArrayLike is the shorter of their
// lengths.
const overlapLength = Math.min(this.length, other.length);
// Check for equivalent elements in their overlaps.
for (let i = 0; i < overlapLength; i ++) {
// If there are any differences between the two i-th elements, then yield
// those differences (prepending the i to the differences’ paths).
for (const { op, path, from, value } of Object.diff(this[i], other[i]);
yield {
op,
path: [ i, ...path ],
from,
value,
};
}
}
// Any remaining elements in this ArrayLike has been “removed” from the
// other ArrayLike.
for (let i = overlapLength; i < this.length; i ++) {
yield {
op: 'remove',
path: i,
from: this[i],
};
}
// Any remaining elements in the other ArrayLike have been “added” from the
// other ArrayLike.
for (let i = overlapLength; i < other.length; i ++) {
yield {
op: 'add',
path: i,
value: other[i],
};
}
}
}
Two rectangles that are equivalent:
Array.from(Object.diff(
new Rectangle(0, 0),
new Rectangle(0, 0),
)
[]
A rectangle and a plain object:
Array.from(Object.diff(
new Rectangle(0, 0),
{},
)
[ { op: 'replace', from: theRectangle, value: thePlainObject } ]
Two rectangles that differ in their widths:
Array.from(Object.diff(
new Rectangle(0, 0),
new Rectangle(0, 1),
)
[ { op: 'replace', path: [ 'width' ], from: 0, value: 1 } ]
Two ArrayLikes that differ in length by 2:
Array.from(Object.diff(
new ArrayLike(2),
new ArrayLike(4),
)
[ { op: 'add', path: [ 2 ], value: undefined },
{ op: 'add', path: [ 3 ], value: undefined } ]
Two length-1 ArrayLikes with Rectangles that differ in their width:
const a0 = new ArrayLike(1);
const a1 = new ArrayLike(1);
a0[0] = new Rectangle(0, 0);
a0[0] = new Rectangle(0, 1);
Array.from(Object.diff(a0, a1)
[ { op: 'replace', path: [ 0, 'width' ], old: 0, value: 1 }