Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/equals.ts
3291 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as arrays from './arrays.js';
7
8
export type EqualityComparer<T> = (a: T, b: T) => boolean;
9
10
/**
11
* Compares two items for equality using strict equality.
12
*/
13
export const strictEquals: EqualityComparer<any> = (a, b) => a === b;
14
15
/**
16
* Checks if the items of two arrays are equal.
17
* By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
18
*/
19
export function itemsEquals<T>(itemEquals: EqualityComparer<T> = strictEquals): EqualityComparer<readonly T[]> {
20
return (a, b) => arrays.equals(a, b, itemEquals);
21
}
22
23
/**
24
* Two items are considered equal, if their stringified representations are equal.
25
*/
26
export function jsonStringifyEquals<T>(): EqualityComparer<T> {
27
return (a, b) => JSON.stringify(a) === JSON.stringify(b);
28
}
29
30
/**
31
* Uses `item.equals(other)` to determine equality.
32
*/
33
export function itemEquals<T extends { equals(other: T): boolean }>(): EqualityComparer<T> {
34
return (a, b) => a.equals(b);
35
}
36
37
/**
38
* Checks if two items are both null or undefined, or are equal according to the provided equality comparer.
39
*/
40
export function equalsIfDefined<T>(v1: T | undefined | null, v2: T | undefined | null, equals: EqualityComparer<T>): boolean;
41
/**
42
* Returns an equality comparer that checks if two items are both null or undefined, or are equal according to the provided equality comparer.
43
*/
44
export function equalsIfDefined<T>(equals: EqualityComparer<T>): EqualityComparer<T | undefined | null>;
45
export function equalsIfDefined<T>(equalsOrV1: EqualityComparer<T> | T, v2?: T | undefined | null, equals?: EqualityComparer<T>): EqualityComparer<T | undefined | null> | boolean {
46
if (equals !== undefined) {
47
const v1 = equalsOrV1 as T | undefined;
48
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
49
return v2 === v1;
50
}
51
return equals(v1, v2);
52
} else {
53
const equals = equalsOrV1 as EqualityComparer<T>;
54
return (v1, v2) => {
55
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
56
return v2 === v1;
57
}
58
return equals(v1, v2);
59
};
60
}
61
}
62
63
/**
64
* Drills into arrays (items ordered) and objects (keys unordered) and uses strict equality on everything else.
65
*/
66
export function structuralEquals<T>(a: T, b: T): boolean {
67
if (a === b) {
68
return true;
69
}
70
71
if (Array.isArray(a) && Array.isArray(b)) {
72
if (a.length !== b.length) {
73
return false;
74
}
75
for (let i = 0; i < a.length; i++) {
76
if (!structuralEquals(a[i], b[i])) {
77
return false;
78
}
79
}
80
return true;
81
}
82
83
if (a && typeof a === 'object' && b && typeof b === 'object') {
84
if (Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
85
const aObj = a as Record<string, unknown>;
86
const bObj = b as Record<string, unknown>;
87
const keysA = Object.keys(aObj);
88
const keysB = Object.keys(bObj);
89
const keysBSet = new Set(keysB);
90
91
if (keysA.length !== keysB.length) {
92
return false;
93
}
94
95
for (const key of keysA) {
96
if (!keysBSet.has(key)) {
97
return false;
98
}
99
if (!structuralEquals(aObj[key], bObj[key])) {
100
return false;
101
}
102
}
103
104
return true;
105
}
106
}
107
108
return false;
109
}
110
111
/**
112
* `getStructuralKey(a) === getStructuralKey(b) <=> structuralEquals(a, b)`
113
* (assuming that a and b are not cyclic structures and nothing extends globalThis Array).
114
*/
115
export function getStructuralKey(t: unknown): string {
116
return JSON.stringify(toNormalizedJsonStructure(t));
117
}
118
119
let objectId = 0;
120
const objIds = new WeakMap<object, number>();
121
122
function toNormalizedJsonStructure(t: unknown): unknown {
123
if (Array.isArray(t)) {
124
return t.map(toNormalizedJsonStructure);
125
}
126
127
if (t && typeof t === 'object') {
128
if (Object.getPrototypeOf(t) === Object.prototype) {
129
const tObj = t as Record<string, unknown>;
130
const res: Record<string, unknown> = Object.create(null);
131
for (const key of Object.keys(tObj).sort()) {
132
res[key] = toNormalizedJsonStructure(tObj[key]);
133
}
134
return res;
135
} else {
136
let objId = objIds.get(t);
137
if (objId === undefined) {
138
objId = objectId++;
139
objIds.set(t, objId);
140
}
141
// Random string to prevent collisions
142
return objId + '----2b76a038c20c4bcc';
143
}
144
}
145
return t;
146
}
147
148