CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/cmp.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { isEqual } from "lodash";
7
8
export function cmp(a: any, b: any): number {
9
if (a < b) {
10
return -1;
11
} else if (a > b) {
12
return 1;
13
}
14
return 0;
15
}
16
17
/*
18
compare two Date | undefined | null objects.
19
20
null and undefined are considered equal to each other.
21
22
null_last:
23
- true: nulls are infinitely in the future
24
- false: nulls are the dawn of mankind
25
*/
26
27
export function cmp_Date(
28
a: Date | undefined | null,
29
b: Date | undefined | null,
30
null_last = false
31
): number {
32
if (a == null) {
33
if (b == null) {
34
return 0;
35
}
36
return null_last ? 1 : -1;
37
}
38
// a != null
39
if (b == null) {
40
return null_last ? -1 : 1;
41
}
42
if (a < b) return -1;
43
if (a > b) return 1;
44
return 0; // note: a == b for Date objects doesn't work as expected, but that's OK here.
45
}
46
47
export function cmp_moment(a?, b?, null_last = false): number {
48
return cmp_Date(a?.toDate(), b?.toDate(), null_last);
49
}
50
51
export function cmp_dayjs(a?, b?, null_last = false): number {
52
return cmp_Date(a?.toDate(), b?.toDate(), null_last);
53
}
54
55
export function cmp_array(a, b): number {
56
const end = Math.max(a.length, b.length);
57
for (let i = 0; i < end; i++) {
58
const c = cmp(a[i], b[i]);
59
if (c) {
60
return c;
61
}
62
}
63
return 0;
64
}
65
66
export function timestamp_cmp(a, b, field): number {
67
if (field == null) {
68
field = "timestamp";
69
}
70
return -cmp_Date(a[field], b[field]);
71
}
72
73
export function field_cmp(field: string | string[]): (a, b) => number {
74
if (typeof field == "string") {
75
return (a, b) => cmp(a[field], b[field]);
76
} else {
77
// array of strings
78
return (a, b) => {
79
for (const f of field) {
80
const c = cmp(a[f], b[f]);
81
if (c) return c;
82
}
83
return 0;
84
};
85
}
86
}
87
88
export function all_fields_equal<T extends { [K: string]: any }>(
89
a: T,
90
b: T,
91
fields: (keyof T)[],
92
verbose?: any
93
) {
94
return !is_different(a, b, fields, verbose);
95
}
96
97
export function is_different<T extends { [K: string]: any }>(
98
a: T,
99
b: T,
100
fields: (keyof T)[],
101
verbose?: any
102
): boolean {
103
if (verbose != null) {
104
return is_different_verbose(a, b, fields, verbose);
105
}
106
let field: keyof T;
107
if (a == null) {
108
if (b == null) {
109
return false; // they are the same
110
}
111
// a not defined but b is
112
for (field of fields) {
113
if (b[field] != null) {
114
return true;
115
}
116
}
117
return false;
118
}
119
if (b == null) {
120
// a is defined or would be handled above
121
for (field of fields) {
122
if (a[field] != null) {
123
return true; // different
124
}
125
}
126
return false; // same
127
}
128
129
for (field of fields) {
130
if (a[field] !== b[field]) {
131
return true;
132
}
133
}
134
return false;
135
}
136
137
// Use for debugging purposes only -- copy code from above to avoid making that
138
// code more complicated and possibly slower.
139
function is_different_verbose(a, b, fields, verbose): boolean {
140
function log(...x) {
141
console.log("is_different_verbose", verbose, ...x);
142
}
143
let field: string;
144
if (a == null) {
145
if (b == null) {
146
log("both null");
147
return false; // they are the same
148
}
149
// a not defined but b is
150
for (field of fields) {
151
if (b[field] != null) {
152
log("a not defined but b is");
153
return true;
154
}
155
}
156
return false;
157
}
158
if (b == null) {
159
// a is defined or would be handled above
160
for (field of fields) {
161
if (a[field] != null) {
162
log(`b null and "${field}" of a is not null`);
163
return true; // different
164
}
165
}
166
return false; // same
167
}
168
169
for (field of fields) {
170
if (a[field] !== b[field]) {
171
log(`field "${field}" differs`, a[field], b[field]);
172
return true;
173
}
174
}
175
log("same");
176
return false;
177
}
178
179
export const is_different_array = (a, b) => !isEqual(a, b);
180
181
// See https://stackoverflow.com/questions/22266826/how-can-i-do-a-shallow-comparison-of-the-properties-of-two-objects-with-javascri/22266891#22266891
182
export const shallowCompare = (obj1, obj2) =>
183
Object.keys(obj1).length === Object.keys(obj2).length &&
184
Object.keys(obj1).every(
185
(key) => obj2.hasOwnProperty(key) && obj1[key] === obj2[key]
186
);
187
188