Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/equals.ts
5237 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
/*
9
* Each function in this file which offers an equality comparison, has an accompanying
10
* `*C` variant which returns an EqualityComparer function.
11
*
12
* The `*C` variant allows for easier composition of equality comparers and improved type-inference.
13
*/
14
15
16
/** Represents a function that decides if two values are equal. */
17
export type EqualityComparer<T> = (a: T, b: T) => boolean;
18
19
export interface IEquatable<T> {
20
equals(other: T): boolean;
21
}
22
23
/**
24
* Compares two items for equality using strict equality.
25
*/
26
export function strictEquals<T>(a: T, b: T): boolean {
27
return a === b;
28
}
29
30
export function strictEqualsC<T>(): EqualityComparer<T> {
31
return (a, b) => a === b;
32
}
33
34
/**
35
* Checks if the items of two arrays are equal.
36
* By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
37
*/
38
export function arrayEquals<T>(a: readonly T[], b: readonly T[], itemEquals?: EqualityComparer<T>): boolean {
39
return arrays.equals(a, b, itemEquals ?? strictEquals);
40
}
41
42
/**
43
* Checks if the items of two arrays are equal.
44
* By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
45
*/
46
export function arrayEqualsC<T>(itemEquals?: EqualityComparer<T>): EqualityComparer<readonly T[]> {
47
return (a, b) => arrays.equals(a, b, itemEquals ?? strictEquals);
48
}
49
50
/**
51
* Drills into arrays (items ordered) and objects (keys unordered) and uses strict equality on everything else.
52
*/
53
export function structuralEquals<T>(a: T, b: T): boolean {
54
if (a === b) {
55
return true;
56
}
57
58
if (Array.isArray(a) && Array.isArray(b)) {
59
if (a.length !== b.length) {
60
return false;
61
}
62
for (let i = 0; i < a.length; i++) {
63
if (!structuralEquals(a[i], b[i])) {
64
return false;
65
}
66
}
67
return true;
68
}
69
70
if (a && typeof a === 'object' && b && typeof b === 'object') {
71
if (Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
72
const aObj = a as Record<string, unknown>;
73
const bObj = b as Record<string, unknown>;
74
const keysA = Object.keys(aObj);
75
const keysB = Object.keys(bObj);
76
const keysBSet = new Set(keysB);
77
78
if (keysA.length !== keysB.length) {
79
return false;
80
}
81
82
for (const key of keysA) {
83
if (!keysBSet.has(key)) {
84
return false;
85
}
86
if (!structuralEquals(aObj[key], bObj[key])) {
87
return false;
88
}
89
}
90
91
return true;
92
}
93
}
94
95
return false;
96
}
97
98
export function structuralEqualsC<T>(): EqualityComparer<T> {
99
return (a, b) => structuralEquals(a, b);
100
}
101
102
/**
103
* `getStructuralKey(a) === getStructuralKey(b) <=> structuralEquals(a, b)`
104
* (assuming that a and b are not cyclic structures and nothing extends globalThis Array).
105
*/
106
export function getStructuralKey(t: unknown): string {
107
return JSON.stringify(toNormalizedJsonStructure(t));
108
}
109
110
let objectId = 0;
111
const objIds = new WeakMap<object, number>();
112
113
function toNormalizedJsonStructure(t: unknown): unknown {
114
if (Array.isArray(t)) {
115
return t.map(toNormalizedJsonStructure);
116
}
117
118
if (t && typeof t === 'object') {
119
if (Object.getPrototypeOf(t) === Object.prototype) {
120
const tObj = t as Record<string, unknown>;
121
const res: Record<string, unknown> = Object.create(null);
122
for (const key of Object.keys(tObj).sort()) {
123
res[key] = toNormalizedJsonStructure(tObj[key]);
124
}
125
return res;
126
} else {
127
let objId = objIds.get(t);
128
if (objId === undefined) {
129
objId = objectId++;
130
objIds.set(t, objId);
131
}
132
// Random string to prevent collisions
133
return objId + '----2b76a038c20c4bcc';
134
}
135
}
136
return t;
137
}
138
139
140
/**
141
* Two items are considered equal, if their stringified representations are equal.
142
*/
143
export function jsonStringifyEquals<T>(a: T, b: T): boolean {
144
return JSON.stringify(a) === JSON.stringify(b);
145
}
146
147
/**
148
* Two items are considered equal, if their stringified representations are equal.
149
*/
150
export function jsonStringifyEqualsC<T>(): EqualityComparer<T> {
151
return (a, b) => JSON.stringify(a) === JSON.stringify(b);
152
}
153
154
/**
155
* Uses `item.equals(other)` to determine equality.
156
*/
157
export function thisEqualsC<T extends IEquatable<T>>(): EqualityComparer<T> {
158
return (a, b) => a.equals(b);
159
}
160
161
/**
162
* Checks if two items are both null or undefined, or are equal according to the provided equality comparer.
163
*/
164
export function equalsIfDefined<T>(v1: T | undefined | null, v2: T | undefined | null, equals: EqualityComparer<T>): boolean {
165
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
166
return v2 === v1;
167
}
168
return equals(v1, v2);
169
}
170
171
/**
172
* Returns an equality comparer that checks if two items are both null or undefined, or are equal according to the provided equality comparer.
173
*/
174
export function equalsIfDefinedC<T>(equals: EqualityComparer<T>): EqualityComparer<T | undefined | null> {
175
return (v1, v2) => {
176
if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
177
return v2 === v1;
178
}
179
return equals(v1, v2);
180
};
181
}
182
183
/**
184
* Each function in this file which offers an equality comparison, has an accompanying
185
* `*C` variant which returns an EqualityComparer function.
186
*
187
* The `*C` variant allows for easier composition of equality comparers and improved type-inference.
188
*/
189
export namespace equals {
190
export const strict = strictEquals;
191
export const strictC = strictEqualsC;
192
193
export const array = arrayEquals;
194
export const arrayC = arrayEqualsC;
195
196
export const structural = structuralEquals;
197
export const structuralC = structuralEqualsC;
198
199
export const jsonStringify = jsonStringifyEquals;
200
export const jsonStringifyC = jsonStringifyEqualsC;
201
202
export const thisC = thisEqualsC;
203
204
export const ifDefined = equalsIfDefined;
205
export const ifDefinedC = equalsIfDefinedC;
206
}
207
208