Skip to content

Commit c9cacd6

Browse files
committed
Added deepEqual
1 parent b4e4057 commit c9cacd6

File tree

5 files changed

+141
-22
lines changed

5 files changed

+141
-22
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
22
"name": "lup-utils",
3-
"version": "1.0.4",
3+
"version": "1.1.0",
44
"description": "NodeJS library providing utility functions.",
55
"main": "./lib/index",
66
"types": "./lib/index.d.ts",
77
"files": [ "lib/**/*" ],
88
"exports": {
99
".": "./lib/index.js",
1010
"./json": "./lib/json.js",
11+
"./objects": "./lib/objects.js",
1112
"./string": "./lib/string.js",
1213
"./terminal": "./lib/terminal.js",
1314
"./utils": "./lib/utils.js"

src/__tests__/object.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { deepEqual } from '../object';
2+
3+
test('deepEqual()', async () => {
4+
// Primitive values (true)
5+
expect(deepEqual(undefined, undefined)).toBe(true);
6+
expect(deepEqual(null, null)).toBe(true);
7+
expect(deepEqual(true, true)).toBe(true);
8+
expect(deepEqual(false, false)).toBe(true);
9+
expect(deepEqual(42, 42)).toBe(true);
10+
expect(deepEqual(0.125, 0.125)).toBe(true);
11+
expect(deepEqual(8, 8.0)).toBe(true);
12+
expect(deepEqual('', '')).toBe(true);
13+
expect(deepEqual('hello', 'hello')).toBe(true);
14+
15+
// Primitive values (false)
16+
expect(deepEqual(true, false)).toBe(false);
17+
expect(deepEqual(null, undefined)).toBe(false);
18+
expect(deepEqual(null, false)).toBe(false);
19+
expect(deepEqual(false, undefined)).toBe(false);
20+
expect(deepEqual(42, '42')).toBe(false);
21+
expect(deepEqual(8, 8.1)).toBe(false);
22+
expect(deepEqual('hello', 'world')).toBe(false);
23+
expect(deepEqual('true', true)).toBe(false);
24+
expect(deepEqual('false', false)).toBe(false);
25+
26+
// Array strict order (true)
27+
expect(deepEqual([], [], true)).toBe(true);
28+
expect(deepEqual([1, 2, 3], [1, 2, 3], true)).toBe(true);
29+
expect(deepEqual(['a', 'b', 'c'], ['a', 'b', 'c'], true)).toBe(true);
30+
expect(deepEqual([1, [2, 3]], [1, [2, 3]], true)).toBe(true);
31+
32+
// Array strict order (false)
33+
expect(deepEqual([1], [], true)).toBe(false);
34+
expect(deepEqual([], [[]], true)).toBe(false);
35+
expect(deepEqual([1, 2, 3], [3, 2, 1], true)).toBe(false);
36+
expect(deepEqual(['a', 'b', 'c'], ['c', 'b', 'a'], true)).toBe(false);
37+
expect(deepEqual([1, [2, 3]], [[1, 2], 3], true)).toBe(false);
38+
expect(deepEqual([1, 2, 3], [4, 5, 6], true)).toBe(false);
39+
40+
// Array unordered (true)
41+
expect(deepEqual([], [], false)).toBe(true);
42+
expect(deepEqual([1, 2, 3], [3, 2, 1], false)).toBe(true);
43+
expect(deepEqual(['a', 'b', 'c'], ['c', 'b', 'a'], false)).toBe(true);
44+
expect(deepEqual([1, [2, 3]], [[3, 2], 1], false)).toBe(true);
45+
46+
// Array unordered (false)
47+
expect(deepEqual([], [1], false)).toBe(false);
48+
expect(deepEqual([[]], [], false)).toBe(false);
49+
expect(deepEqual([1, 2, 3], [4, 5, 6], false)).toBe(false);
50+
expect(deepEqual([1, 2, 2], [2, 1, 1], false)).toBe(false);
51+
52+
// Object strict order (true)
53+
expect(deepEqual({}, {}, true)).toBe(true);
54+
expect(deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 }, true)).toBe(true);
55+
expect(deepEqual({ a: 1, b: { c: 3 } }, { a: 1, b: { c: 3 } }, true)).toBe(true);
56+
expect(deepEqual({ a: [1, 2], b: 3 }, { a: [1, 2], b: 3 }, true)).toBe(true);
57+
58+
// Object strict order (false)
59+
expect(deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }, true)).toBe(false);
60+
expect(deepEqual({ a: 1, b: { c: 3 } }, { b: { c: 3 }, a: 1 }, true)).toBe(false);
61+
expect(deepEqual({ a: [1, 2], b: 3 }, { b: 3, a: [1, 2] }, true)).toBe(false);
62+
expect(deepEqual({ a: 1, b: 2 }, { a: 1, b: 3 }, true)).toBe(false);
63+
64+
// Object unordered (true)
65+
expect(deepEqual({}, {}, false)).toBe(true);
66+
expect(deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }, false)).toBe(true);
67+
expect(deepEqual({ a: 1, b: { c: 3 } }, { b: { c: 3 }, a: 1 }, false)).toBe(true);
68+
expect(deepEqual({ a: [1, 2], b: 3 }, { b: 3, a: [1, 2] }, false)).toBe(true);
69+
70+
// Object unordered (false)
71+
expect(deepEqual({ a: 1, b: 2 }, { a: 1, b: 3 }, false)).toBe(false);
72+
expect(deepEqual({ a: 1, b: 2 }, { a: 1 })).toBe(false);
73+
expect(deepEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false);
74+
});

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
export * from './json';
2+
export * from './object';
23
export * from './string';
34
export * from './terminal';
45
export * from './utils';
56

67
import * as json from './json';
8+
import * as object from './object';
79
import * as string from './string';
810
import * as terminal from './terminal';
911
import * as utils from './utils';
@@ -13,6 +15,7 @@ import * as utils from './utils';
1315
*/
1416
const lupUtils = {
1517
...json,
18+
...object,
1619
...string,
1720
...terminal,
1821
...utils,

src/object.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Deeply compares two values for equality.
3+
* @param a Object to compare.
4+
* @param b Object to compare.
5+
* @param strictOrder If true, the order of items in arrays as well as object keys matters.
6+
* @returns True if the objects are equal, false otherwise.
7+
*/
8+
export function deepEqual(a: any, b: any, strictOrder: boolean = false): boolean {
9+
if (a === b) return true;
10+
if (typeof a !== typeof b) return false;
11+
if (Array.isArray(a)) {
12+
if (!Array.isArray(b) || a.length !== b.length) return false;
13+
if (strictOrder) {
14+
for (let i = 0; i < a.length; i++) {
15+
if (!deepEqual(a[i], b[i], strictOrder)) return false;
16+
}
17+
} else {
18+
const bRemaining = [...b];
19+
for (const itemA of a) {
20+
const indexB = bRemaining.findIndex((itemB) => deepEqual(itemA, itemB, strictOrder));
21+
if (indexB === -1) return false;
22+
bRemaining.splice(indexB, 1);
23+
}
24+
}
25+
return true;
26+
}
27+
if (typeof a === 'object' && a !== null && b !== null) {
28+
const keysA = Object.keys(a);
29+
const keysB = Object.keys(b);
30+
if (keysA.length !== keysB.length) return false;
31+
if (strictOrder && !deepEqual(keysA, keysB, strictOrder)) return false;
32+
for (const key of keysA) {
33+
if (!deepEqual(a[key], b[key], strictOrder)) return false;
34+
}
35+
return true;
36+
}
37+
return false;
38+
}

src/string.ts

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
* @param options Options for converting the string.
55
* @returns Human-readable string representation of the byte value as bits per second (e.g. "100 Mbps").
66
*/
7-
export function byteValueToBitsHumanString(bytesPerSec: number, options?: {
8-
9-
/** Number of decimal places to include (default 0). */
10-
precision?: number,
11-
12-
}): string {
7+
export function byteValueToBitsHumanString(
8+
bytesPerSec: number,
9+
options?: {
10+
/** Number of decimal places to include (default 0). */
11+
precision?: number;
12+
},
13+
): string {
1314
const bits = bytesPerSec * 8;
1415
const base = 1000;
1516
const units = ['bits', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps'];
@@ -26,24 +27,26 @@ export function byteValueToBitsHumanString(bytesPerSec: number, options?: {
2627

2728
/**
2829
* Converts a byte value to a human-readable string.
29-
*
30+
*
3031
* @param bytes Value in bytes to convert to a human-readable string.
3132
* @param options Options for converting the string.
3233
* @returns Human-readable string representation of the byte value (e.g. )
3334
*/
34-
export function byteValueToHumanString(bytes: number, options?: {
35-
36-
/** Whether to use the decimal (base 1000) or the binary (base 1024) for conversion (default 1024). */
37-
decimalBase?: boolean;
38-
39-
/** If the label should be binary (e.g. "GiB" vs "GB") if decimalBase is false (default false). */
40-
binaryLabel?: boolean;
41-
42-
/** Number of decimal places to include (default 0). */
43-
precision?: number;
44-
45-
}): string {
46-
const units = (!options?.decimalBase && options?.binaryLabel)
35+
export function byteValueToHumanString(
36+
bytes: number,
37+
options?: {
38+
/** Whether to use the decimal (base 1000) or the binary (base 1024) for conversion (default 1024). */
39+
decimalBase?: boolean;
40+
41+
/** If the label should be binary (e.g. "GiB" vs "GB") if decimalBase is false (default false). */
42+
binaryLabel?: boolean;
43+
44+
/** Number of decimal places to include (default 0). */
45+
precision?: number;
46+
},
47+
): string {
48+
const units =
49+
!options?.decimalBase && options?.binaryLabel
4750
? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']
4851
: ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
4952
const step = options?.decimalBase ? 1000 : 1024;
@@ -55,7 +58,7 @@ export function byteValueToHumanString(bytes: number, options?: {
5558
i++;
5659
}
5760

58-
return humanReadable.toFixed(options?.precision ?? 0)+' '+units[i];
61+
return humanReadable.toFixed(options?.precision ?? 0) + ' ' + units[i];
5962
}
6063

6164
/**

0 commit comments

Comments
 (0)