Skip to content

Instantly share code, notes, and snippets.

@netcall-jlo
Last active August 9, 2024 15:27
Show Gist options
  • Save netcall-jlo/9f78553b0f6a5894d3c28c3bd638fc8d to your computer and use it in GitHub Desktop.
Save netcall-jlo/9f78553b0f6a5894d3c28c3bd638fc8d to your computer and use it in GitHub Desktop.
// isNonNullObject()
console.log('Testing isNonNullObject() ...');
console.assert(isNonNullObject({}), "An Object should be a non-null object");
console.assert(isNonNullObject([]), "An Array should be a non-null object");
console.assert(isNonNullObject(new WeakMap()), "A WeakMap should be a non-null object");
console.assert(!isNonNullObject(null), "null should not be a non-null object");
console.assert(!isNonNullObject(''), "A String should not be a non-null object");
console.log('... complete!');
// isIterable()
console.log('Testing isIterable() ...');
console.assert(isIterable([]), "An Array should be iterable");
console.assert(isIterable([0]), "A populated Array should be iterable");
console.assert(isIterable(document.querySelectorAll('body')), "A NodeList should be iterable");
console.assert(isIterable(document.getElementsByTagName('body')), "A live NodeList should be iterable");
console.assert(isIterable(new Set()), "A Set should be iterable");
console.assert(isIterable(new Uint8Array()), "An 8-bit Array should be iterable");
console.assert(isIterable(new Uint16Array()), "A 16-bit Array should be iterable");
console.assert(isIterable(new Uint32Array()), "A 32-bit Array should be iterable");
console.assert(isIterable(new Map()), "A Map should be iterable");
console.assert(!isIterable({}), "An object should not be iterable");
console.assert(!isIterable({ length: 0 }), "A length should not be enough");
console.assert(!isIterable(document.querySelector('body')), "A Node should not be iterable");
console.assert(!isIterable('asd'), "A String should not be iterable");
console.assert(!isIterable(0), "A Number should not be iterable");
console.assert(!isIterable(new WeakMap()), "A WeakMap should not be iterable");
console.log('... complete!');
// keysMatch()
console.log('Testing keysMatch() ...');
console.assert(keysMatch([], []), "Empty arrays don't have the same keys");
console.assert(keysMatch([1], [2]), "Arrays don't have the same keys");
console.assert(keysMatch({ a: 1 }, { a: 2 }), "Objects don't have the same keys");
console.assert(keysMatch({ a: 1, b: 2 }, { b: 1, a: 2 }), "Objects with different orders don't have the same keys");
console.assert(keysMatch('012', [0, 1, 2]), "Arrays and Strings don't have the same keys");
console.assert(keysMatch(document.querySelectorAll('body'), document.getElementsByTagName('body')), "NodeLists and live NodeLists don't have the same keys");
console.assert(keysMatch(document.querySelectorAll('body'), [0]), "NodeLists and Arrays don't have the same keys");
console.assert(!keysMatch({ a: 1 }, { a: 1, b: 2 }), "Objects have the same keys as bigger objects");
console.assert(!keysMatch({ a: 1, b: 1 }, { a: 1 }), "Bigger objects have the same keys as smaller ones");
console.log('... complete!');
// looksLike()
console.log('Testing looksLike() ...');
// First check:
console.assert(looksLike(0, 0), "Numbers don't look the same");
console.assert(looksLike(Number.NaN, Number('a')), "NaN doesn't look like NaN");
console.assert(looksLike(Number.POSITIVE_INFINITY, 1 / 0), "Infinity doesn't look like Infinity");
console.assert(looksLike('asd', 'asd'), "Strings don't look the same");
console.assert(looksLike('0', '0'), "Numeric strings don't look the same");
console.assert(!looksLike(0, 1), "Different numbers look the same");
console.assert(!looksLike('asd', 'qwe'), "Different strings look the same");
console.assert(!looksLike('0', '1'), "Different numeric strings look the same");
console.assert(!looksLike('0', 0), "Numbers and numeric strings look the same");
// Second check:
console.assert(looksLike([1], [1]), "Arrays don't look the same");
console.assert(looksLike([[1]], [[1]]), "Nested arrays don't look the same");
console.assert(looksLike([document.body], document.querySelectorAll('body')), "Arrays don't look like array-likes");
console.assert(looksLike(document.getElementsByTagName('body'), document.querySelectorAll('body')), "Array-likess don't look the same");
(() => { const a1 = []; a1[1] = 1; const a2 = []; a2[1] = 1; console.assert(looksLike(a1, a2), "Sparse arrays don't look alike"); })();
console.assert(!looksLike([0], [1]), "Array items don't get checked");
(() => { const a1 = []; a1[1] = 1; const a2 = [0, 1]; console.assert(!looksLike(a1, a2), "Sparse arrays look like dense arrays"); })();
(() => { const a1 = [0, 1]; const a2 = []; a2[1] = 1; console.assert(!looksLike(a1, a2), "Dense arrays look like sparse arrays"); })();
(() => { const a1 = [0, 1, 2]; delete a1[0]; const a2 = [0, 1, 2]; delete a2[1]; console.assert(!looksLike(a1, a2), "Sparse arrays with different games look alike"); })();
console.assert(!looksLike(['0', '1', '2'], '012'), "Arrays and strings look alike");
// Third check:
console.assert(looksLike({ a: 1 }, { a: 1 }), "Objects don't look the same");
console.assert(looksLike(window.document, document), "Named objects don't look the same");
console.assert(looksLike({ a: 1, b: 2 }, { b: 2, a: 1 }), "Objects with the same keys in different orders don't look the same");
console.assert(looksLike({ a: [1] }, { a: [1] }), "Nested objects don't look the same");
console.assert(looksLike({ a: { b : 1 } }, { a: { b : 1 } }), "Nested objects don't look the same");
console.assert(looksLike({ a: { b : [1] } }, { a: { b : [1] } }), "Nested objects don't look the same");
console.assert(!looksLike({ a: 1 }, { a: 1, b: 1 }), "Objects looks like larger objects");
console.assert(!looksLike({ a: 1, b: 1 }, { a: 1 }), "Larger objects looks like smaller objects");
console.assert(!looksLike({ a: [0] }, { a: [1] }), "Nested objects aren't checked");
console.assert(!looksLike([], { length: 0 }), "An Array looks like an Object");
console.assert(!looksLike({ length: 0 }, []), "An Object looks like an Array");
console.assert(!looksLike([], ''), "An Array looks like a String");
console.assert(!looksLike('', []), "A String looks like an Array");
console.log('... complete!');
function isNonNullObject(object: unknown): object is Record<PropertyKey, any> {
return object !== null && typeof object === 'object';
}
function isIterable(source: unknown): source is any[] {
return (
isNonNullObject(source)
&& typeof (source as any[])[Symbol.iterator] === 'function'
);
}
function keysMatch(
source: Record<PropertyKey, any>,
check: Record<PropertyKey, any>,
) {
const sourceKeys = Object.keys(source).sort();
const checkKeys = Object.keys(check).sort();
return (
sourceKeys.length === checkKeys.length
&& sourceKeys.every((key, index) => checkKeys[index] === key)
);
}
function looksLike(source: unknown, check: unknown): boolean {
// First check: are they the same or do they pass `Object.is()`?
if (source === check || Object.is(source, check)) {
return true;
}
// Second check: are they both iterable, have the same length, and contain
// the same items in the same order with the same gaps (if any)?
if (isIterable(source)) {
return (
isIterable(check)
&& source.length === check.length
&& keysMatch(source, check)
&& Array.prototype.every.call(
source,
(item: any, index: number) => looksLike(item, check[index]),
)
);
}
// Third check: are they both objects that contain the same properties and
// those properties contain values the look the same?
if (isNonNullObject(source)) {
return (
isNonNullObject(check)
&& keysMatch(source, check)
&& Object.entries(source).every(([property, value]) => (
looksLike(value, check[property])
))
);
}
// Finally: these two items do not look the same.
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment