UNPKG

5.08 kBJavaScriptView Raw
1/* @flow weak */
2"use strict";
3
4var isArray = Array.isArray;
5function isObject(o) {
6 /* eslint-disable no-new-object */
7 return new Object(o) === o;
8 /* eslint-enable no-new-object */
9}
10
11/**
12 ### Utility functions
13
14 Utility functions are exposed (and documented) only to make contributions to jsverify more easy.
15 The changes here don't follow semver, i.e. there might be backward-incompatible changes even in patch releases.
16
17 Use [underscore.js](http://underscorejs.org/), [lodash](https://lodash.com/), [ramda](http://ramda.github.io/ramdocs/docs/), [lazy.js](http://danieltao.com/lazy.js/) or some other utility belt.
18*/
19
20/**
21 - `utils.isEqual(x: json, y: json): bool`
22
23 Equality test for `json` objects.
24*/
25function isEqual(a, b) {
26 var i;
27
28 if (a === b) {
29 return true;
30 } else if (isArray(a) && isArray(b) && a.length === b.length) {
31 for (i = 0; i < a.length; i++) {
32 if (!isEqual(a[i], b[i])) {
33 return false;
34 }
35 }
36 return true;
37 } else if (isObject(a) && isObject(b) && !isArray(a) && !isArray(b)) {
38 var akeys = Object.keys(a);
39 var bkeys = Object.keys(b);
40 if (!isEqual(akeys, bkeys)) {
41 return false;
42 }
43
44 for (i = 0; i < akeys.length; i++) {
45 if (!isEqual(a[akeys[i]], b[akeys[i]])) {
46 return false;
47 }
48 }
49 return true;
50 }
51
52 return false;
53}
54
55/**
56 - `utils.isApproxEqual(x: a, y: b, opts: obj): bool`
57
58 Tests whether two objects are approximately and optimistically equal.
59 Returns `false` only if they are distinguisable not equal.
60 This function works with cyclic data.
61
62 Takes optional 'opts' parameter with properties:
63
64 - `fnEqual` - whether all functions are considered equal (default: yes)
65 - `depth` - how deep to recurse until treating as equal (default: 5)
66*/
67function isApproxEqual(x, y, opts) {
68 opts = opts || {};
69 var fnEqual = opts.fnEqual === false ? false : true;
70 var depth = opts.depth || 5; // totally arbitrary
71
72 // state contains pairs we checked (or are still checking, but assume equal!)
73 var state = [];
74
75 function loop(a, b, n) {
76 // trivial check
77 if (a === b) {
78 return true;
79 }
80
81 // depth check
82 if (n >= depth) {
83 return true;
84 }
85
86 var i;
87
88 // check if pair already occured
89 for (i = 0; i < state.length; i++) {
90 if (state[i][0] === a && state[i][1] === b) {
91 return true;
92 }
93 }
94
95 // add to state
96 state.push([a, b]);
97
98 if (typeof a === "function" && typeof b === "function") {
99 return fnEqual;
100 }
101
102 if (isArray(a) && isArray(b) && a.length === b.length) {
103 for (i = 0; i < a.length; i++) {
104 if (!loop(a[i], b[i], n + 1)) {
105 return false;
106 }
107 }
108 return true;
109 } else if (isObject(a) && isObject(b) && !isArray(a) && !isArray(b)) {
110 var akeys = Object.keys(a);
111 var bkeys = Object.keys(b);
112 if (!loop(akeys, bkeys, n + 1)) {
113 return false;
114 }
115
116 for (i = 0; i < akeys.length; i++) {
117 if (!loop(a[akeys[i]], b[akeys[i]], n + 1)) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 return false;
125 }
126 return loop(x, y, 0);
127}
128
129function identity(x) {
130 return x;
131}
132
133function pluck(arr, key) {
134 return arr.map(function (e) {
135 return e[key];
136 });
137}
138
139/**
140 - `utils.force(x: a | () -> a) : a`
141
142 Evaluate `x` as nullary function, if it is one.
143*/
144function force(arb) {
145 return (typeof arb === "function") ? arb() : arb;
146}
147
148/**
149 - `utils.merge(x... : obj): obj`
150
151 Merge two objects, a bit like `_.extend({}, x, y)`.
152*/
153function merge() {
154 var res = {};
155
156 for (var i = 0; i < arguments.length; i++) {
157 var arg = arguments[i];
158 var keys = Object.keys(arg);
159 for (var j = 0; j < keys.length; j++) {
160 var key = keys[j];
161 res[key] = arg[key];
162 }
163 }
164
165 return res;
166}
167
168function div2(x) {
169 return Math.floor(x / 2);
170}
171
172function log2(x) {
173 return Math.log(x) / Math.log(2);
174}
175
176function ilog2(x) {
177 return x <= 0 ? 0 : Math.floor(log2(x));
178}
179
180function curriedN(n) {
181 var n1 = n - 1;
182 return function curriedNInstance(result, args) {
183 if (args.length === n) {
184 return result(args[n1]);
185 } else {
186 return result;
187 }
188 };
189}
190
191var curried2 = curriedN(2);
192var curried3 = curriedN(3);
193
194function charArrayToString(arr) {
195 return arr.join("");
196}
197
198function stringToCharArray(str) {
199 return str.split("");
200}
201
202function pairArrayToDict(arrayOfPairs) {
203 var res = {};
204 arrayOfPairs.forEach(function (p) {
205 res[p[0]] = p[1];
206 });
207 return res;
208}
209
210function dictToPairArray(m) {
211 var res = [];
212 Object.keys(m).forEach(function (k) {
213 res.push([k, m[k]]);
214 });
215 return res;
216}
217
218module.exports = {
219 isArray: isArray,
220 isObject: isObject,
221 isEqual: isEqual,
222 isApproxEqual: isApproxEqual,
223 identity: identity,
224 pluck: pluck,
225 force: force,
226 merge: merge,
227 div2: div2,
228 ilog2: ilog2,
229 curried2: curried2,
230 curried3: curried3,
231 charArrayToString: charArrayToString,
232 stringToCharArray: stringToCharArray,
233 pairArrayToDict: pairArrayToDict,
234 dictToPairArray: dictToPairArray,
235};