spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[FREQUENCY] formula added and tested
author
Ben Vogt <[email protected]>
date
2017-06-19 01:41:34
stats
9 file(s) changed, 172 insertions(+), 5 deletions(-)
files
DOCS.md
TODO.md
dist/Formulas/AllFormulas.js
dist/Formulas/Range.js
src/Formulas/AllFormulas.ts
src/Formulas/Range.ts
tests/Formulas/RangeTest.ts
tests/SheetFormulaTest.ts
tests/Utils/Asserts.ts
  1diff --git a/DOCS.md b/DOCS.md
  2index e0ac0a2..6a63693 100644
  3--- a/DOCS.md
  4+++ b/DOCS.md
  5@@ -1234,6 +1234,18 @@
  6 @returns {number} 
  7 @constructor
  8 ```
  9+## Range
 10+
 11+
 12+### FREQUENCY 
 13+
 14+```
 15+  Calculates the frequency distribution of a range into specified classes or "bins". 
 16+@param range - to get frequency for. 
 17+@param bins - or classes. 
 18+@returns {Array<number>} 
 19+@constructor
 20+```
 21 ## Statistical
 22 
 23 
 24diff --git a/TODO.md b/TODO.md
 25index b2bbc48..c8fb1fb 100644
 26--- a/TODO.md
 27+++ b/TODO.md
 28@@ -130,7 +130,6 @@ For example 64 tbs to a qt.
 29 * TEXT
 30 * UPPER
 31 * VALUE
 32-* FREQUENCY
 33 * GROWTH
 34 * LINEST
 35 * LOGEST
 36diff --git a/dist/Formulas/AllFormulas.js b/dist/Formulas/AllFormulas.js
 37index 41b3da6..8e019cd 100644
 38--- a/dist/Formulas/AllFormulas.js
 39+++ b/dist/Formulas/AllFormulas.js
 40@@ -73,6 +73,8 @@ exports.UPLUS = Math_1.UPLUS;
 41 exports.UMINUS = Math_1.UMINUS;
 42 exports.MROUND = Math_1.MROUND;
 43 exports.FACTDOUBLE = Math_1.FACTDOUBLE;
 44+var Range_1 = require("./Range");
 45+exports.FREQUENCY = Range_1.FREQUENCY;
 46 var Info_1 = require("./Info");
 47 exports.NA = Info_1.NA;
 48 exports.ISTEXT = Info_1.ISTEXT;
 49diff --git a/dist/Formulas/Range.js b/dist/Formulas/Range.js
 50new file mode 100644
 51index 0000000..493a0b1
 52--- /dev/null
 53+++ b/dist/Formulas/Range.js
 54@@ -0,0 +1,56 @@
 55+"use strict";
 56+exports.__esModule = true;
 57+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
 58+var Filter_1 = require("../Utilities/Filter");
 59+var TypeConverter_1 = require("../Utilities/TypeConverter");
 60+/**
 61+ * Calculates the frequency distribution of a range into specified classes or "bins".
 62+ * @param range - to get frequency for.
 63+ * @param bins - or classes.
 64+ * @returns {Array<number>}
 65+ * @constructor
 66+ */
 67+var FREQUENCY = function (range, bins) {
 68+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "FREQUENCY");
 69+    if (!Array.isArray(bins)) {
 70+        bins = [bins];
 71+    }
 72+    if (!Array.isArray(range)) {
 73+        range = [range];
 74+    }
 75+    bins = Filter_1.Filter.flattenAndThrow(bins).map(function (value) {
 76+        return TypeConverter_1.TypeConverter.firstValueAsNumber(value);
 77+    }).sort(function (a, b) {
 78+        return a - b;
 79+    });
 80+    range = Filter_1.Filter.flattenAndThrow(range).map(function (value) {
 81+        return TypeConverter_1.TypeConverter.firstValueAsNumber(value);
 82+    }).sort(function (a, b) {
 83+        return a - b;
 84+    });
 85+    var n = range.length;
 86+    var b = bins.length;
 87+    var r = [];
 88+    for (var i = 0; i <= b; i++) {
 89+        r[i] = 0;
 90+        for (var j = 0; j < n; j++) {
 91+            if (i === 0) {
 92+                if (range[j] <= bins[0]) {
 93+                    r[0] += 1;
 94+                }
 95+            }
 96+            else if (i < b) {
 97+                if (range[j] > bins[i - 1] && range[j] <= bins[i]) {
 98+                    r[i] += 1;
 99+                }
100+            }
101+            else if (i === b) {
102+                if (range[j] > bins[b - 1]) {
103+                    r[b] += 1;
104+                }
105+            }
106+        }
107+    }
108+    return r;
109+};
110+exports.FREQUENCY = FREQUENCY;
111diff --git a/src/Formulas/AllFormulas.ts b/src/Formulas/AllFormulas.ts
112index 8402b80..6946d58 100644
113--- a/src/Formulas/AllFormulas.ts
114+++ b/src/Formulas/AllFormulas.ts
115@@ -72,6 +72,9 @@ import {
116   MROUND,
117   FACTDOUBLE
118 } from "./Math";
119+import {
120+  FREQUENCY
121+} from "./Range";
122 import {
123   NA,
124   ISTEXT,
125@@ -339,5 +342,6 @@ export {
126   ISNUMBER,
127   ISNONTEXT,
128   MROUND,
129-  FACTDOUBLE
130+  FACTDOUBLE,
131+  FREQUENCY
132 }
133\ No newline at end of file
134diff --git a/src/Formulas/Range.ts b/src/Formulas/Range.ts
135new file mode 100644
136index 0000000..599103d
137--- /dev/null
138+++ b/src/Formulas/Range.ts
139@@ -0,0 +1,60 @@
140+import {
141+  ArgsChecker
142+} from "../Utilities/ArgsChecker";
143+import {Filter} from "../Utilities/Filter";
144+import {TypeConverter} from "../Utilities/TypeConverter";
145+
146+
147+/**
148+ * Calculates the frequency distribution of a range into specified classes or "bins".
149+ * @param range - to get frequency for.
150+ * @param bins - or classes.
151+ * @returns {Array<number>}
152+ * @constructor
153+ */
154+var FREQUENCY = function (range, bins) : Array<number> {
155+  ArgsChecker.checkLength(arguments, 2, "FREQUENCY");
156+  if (!Array.isArray(bins)) {
157+    bins = [bins];
158+  }
159+  if (!Array.isArray(range)) {
160+    range = [range];
161+  }
162+  bins = Filter.flattenAndThrow(bins).map(function (value) {
163+    return TypeConverter.firstValueAsNumber(value);
164+  }).sort(function (a, b) {
165+    return a - b;
166+  });
167+  range = Filter.flattenAndThrow(range).map(function (value) {
168+    return TypeConverter.firstValueAsNumber(value);
169+  }).sort(function (a, b) {
170+    return a - b;
171+  });
172+
173+  var n = range.length;
174+  var b = bins.length;
175+  var r = [];
176+  for (var i = 0; i <= b; i++) {
177+    r[i] = 0;
178+    for (var j = 0; j < n; j++) {
179+      if (i === 0) {
180+        if (range[j] <= bins[0]) {
181+          r[0] += 1;
182+        }
183+      } else if (i < b) {
184+        if (range[j] > bins[i - 1] && range[j] <= bins[i]) {
185+          r[i] += 1;
186+        }
187+      } else if (i === b) {
188+        if (range[j] > bins[b - 1]) {
189+          r[b] += 1;
190+        }
191+      }
192+    }
193+  }
194+  return r;
195+};
196+
197+export {
198+  FREQUENCY
199+}
200\ No newline at end of file
201diff --git a/tests/Formulas/RangeTest.ts b/tests/Formulas/RangeTest.ts
202new file mode 100644
203index 0000000..d7604d3
204--- /dev/null
205+++ b/tests/Formulas/RangeTest.ts
206@@ -0,0 +1,28 @@
207+import {
208+  FREQUENCY
209+} from "../../src/Formulas/Range";
210+import {
211+  assertArrayEquals,
212+  catchAndAssertEquals,
213+  test
214+} from "../Utils/Asserts";
215+import * as ERRORS from "../../src/Errors";
216+
217+
218+
219+test("FREQUENCY", function(){
220+  assertArrayEquals(FREQUENCY([10, 2, 3, 44, 1, 2], 22), [5, 1]);
221+  assertArrayEquals(FREQUENCY([10, 2, 3, 44, 1, 2], [22]), [5, 1]);
222+  assertArrayEquals(FREQUENCY([10, [2, 3, 44, 1], 2], [22]), [5, 1]);
223+  assertArrayEquals(FREQUENCY([18, 30, 90, 91, 35, 27, 75, 28, 58], [25, 50, 75]), [1, 4, 2, 2]);
224+  assertArrayEquals(FREQUENCY([18, 30, 90, 91, 35, 27, 75, 28, 58], [50, 25, 75]), [1, 4, 2, 2]);
225+  catchAndAssertEquals(function() {
226+    FREQUENCY.apply(this, [10, 10, 10]);
227+  }, ERRORS.NA_ERROR);
228+  catchAndAssertEquals(function() {
229+    FREQUENCY.apply(this, [10]);
230+  }, ERRORS.NA_ERROR);
231+  catchAndAssertEquals(function() {
232+    FREQUENCY([10, 2, 3, 44, 1, [], 2], 22);
233+  }, ERRORS.REF_ERROR);
234+});
235\ No newline at end of file
236diff --git a/tests/SheetFormulaTest.ts b/tests/SheetFormulaTest.ts
237index fa889a5..fe0f265 100644
238--- a/tests/SheetFormulaTest.ts
239+++ b/tests/SheetFormulaTest.ts
240@@ -35,7 +35,7 @@ function assertFormulaResultsInType(formula: string, type: string) {
241   assertEquals(typeof cell.getValue(), type);
242 }
243 
244-function testFormulaToArray(formula: string, expectation: any) {
245+function assertFormulaEqualsArray(formula: string, expectation: any) {
246   var sheet  = new Sheet();
247   sheet.setCell("A1", formula);
248   var cell = sheet.getCell("A1");
249@@ -529,7 +529,7 @@ test("Sheet SINH", function(){
250 });
251 
252 test("Sheet SPLIT", function(){
253-  testFormulaToArray('=SPLIT("1,2,3", ",", TRUE)', [ '1', '2', '3' ]);
254+  assertFormulaEqualsArray('=SPLIT("1,2,3", ",", TRUE)', [ '1', '2', '3' ]);
255 });
256 
257 test("Sheet SQRT", function(){
258@@ -679,6 +679,12 @@ test("Sheet FACTDOUBLE", function(){
259   assertFormulaEquals('=FACTDOUBLE(7)', 105);
260 });
261 
262+
263+test("Sheet FREQUENCY", function(){
264+  assertFormulaEqualsArray('=FREQUENCY([10, 2, 3, 44, 1, 2], 22)', [5, 1]);
265+});
266+
267+
268 test("Sheet *", function(){
269   assertFormulaEquals('= 10 * 10', 100);
270   assertFormulaEquals('= 10 * 0', 0);
271diff --git a/tests/Utils/Asserts.ts b/tests/Utils/Asserts.ts
272index 2740d3e..3709262 100644
273--- a/tests/Utils/Asserts.ts
274+++ b/tests/Utils/Asserts.ts
275@@ -19,6 +19,7 @@ function assertArrayEquals(actual: Array<any>, expected: Array<any>, ) {
276   if (expected.length != actual.length) {
277     console.log("expected: ", expected, " actual:", actual);
278     console.trace();
279+    return;
280   }
281   for (var index in expected) {
282     if (expected[index] != actual[index]) {