summaryrefslogtreecommitdiff
path: root/packages/backend/src/misc/diff-arrays.ts
blob: b50ca1d4f7173f8f3c6abb846b17cf2bbf2d7bde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 */

export interface DiffResult<T> {
	added: T[];
	removed: T[];
}

/**
 * Calculates the difference between two snapshots of data.
 * Null, undefined, and empty arrays are supported, and duplicate values are ignored.
 * Result sets are de-duplicated, and will be empty if no data was added or removed (respectively).
 * The inputs are treated as un-ordered, so a re-ordering of the same data will NOT be considered a change.
 * @param dataBefore Array containing data before the change
 * @param dataAfter Array containing data after the change
 */
export function diffArrays<T>(dataBefore: T[] | null | undefined, dataAfter: T[] | null | undefined): DiffResult<T> {
	const before = dataBefore ? new Set(dataBefore) : null;
	const after = dataAfter ? new Set(dataAfter) : null;

	// data before AND after => changed
	if (before?.size && after?.size) {
		const added: T[] = [];
		const removed: T[] = [];

		for (const host of before) {
			// before and NOT after => removed
			// delete operation removes duplicates to speed up the "after" loop
			if (!after.delete(host)) {
				removed.push(host);
			}
		}

		for (const host of after) {
			// after and NOT before => added
			if (!before.has(host)) {
				added.push(host);
			}
		}

		return { added, removed };
	}

	// data ONLY before => all removed
	if (before?.size) {
		return { added: [], removed: Array.from(before) };
	}

	// data ONLY after => all added
	if (after?.size) {
		return { added: Array.from(after), removed: [] };
	}

	// data NEITHER before nor after => no change
	return { added: [], removed: [] };
}

/**
 * Checks for any difference between two snapshots of data.
 * Null, undefined, and empty arrays are supported, and duplicate values are ignored.
 * The inputs are treated as un-ordered, so a re-ordering of the same data will NOT be considered a change.
 * @param dataBefore Array containing data before the change
 * @param dataAfter Array containing data after the change
 */
export function diffArraysSimple<T>(dataBefore: T[] | null | undefined, dataAfter: T[] | null | undefined): boolean {
	const before = dataBefore ? new Set(dataBefore) : null;
	const after = dataAfter ? new Set(dataAfter) : null;

	if (before?.size && after?.size) {
		// different size => changed
		if (before.size !== after.size) return true;

		// removed => changed
		for (const host of before) {
			// delete operation removes duplicates to speed up the "after" loop
			if (!after.delete(host)) {
				return true;
			}
		}

		// added => changed
		for (const host of after) {
			if (!before.has(host)) {
				return true;
			}
		}

		// identical values => no change
		return false;
	}

	// before and NOT after => change
	if (before?.size) return true;

	// after and NOT before => change
	if (after?.size) return true;

	// NEITHER before nor after => no change
	return false;
}