spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
Added Formula.COUNTIFS
author
Ben Vogt <[email protected]>
date
2017-02-15 02:58:27
stats
3 file(s) changed, 69 insertions(+), 2 deletions(-)
files
src/RawFormulas/RawFormulas.ts
src/RawFormulas/Utils.ts
tests/FormulasTest.ts
  1diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
  2index b5404e4..45fcb40 100644
  3--- a/src/RawFormulas/RawFormulas.ts
  4+++ b/src/RawFormulas/RawFormulas.ts
  5@@ -80,7 +80,6 @@ var COMBIN = Formula["COMBIN"];
  6 var CONCATENATE = Formula["CONCATENATE"];
  7 var CONVERT = Formula["CONVERT"];
  8 var CORREL = Formula["CORREL"];
  9-var COUNTIFS = Formula["COUNTIFS"];
 10 var COUNTUNIQUE = Formula["COUNTUNIQUE"];
 11 var COVARIANCEP = Formula["COVARIANCEP"];
 12 var COVARIANCES = Formula["COVARIANCES"];
 13@@ -180,6 +179,39 @@ var COUNTIF = function (...values) {
 14   return count;
 15 };
 16 
 17+var COUNTIFS = function (...values) {
 18+  checkArgumentsAtLeastLength(values, 2);
 19+  var criteriaEvaluationFunctions = values.map(function (criteria, index) {
 20+    if (index % 2 === 1) {
 21+      return CriteriaFunctionFactory.createCriteriaFunction(criteria);
 22+    } else {
 23+      return function () {return false;}
 24+    }
 25+  });
 26+
 27+  var count = 0;
 28+  // For every value in the range
 29+  for (var i = 0; i < values[0].length; i++) {
 30+    // check for criteria eval for other ranges and other criteria pairs
 31+    var otherCriteriaEvaluationSuccessfulSoFar = true;
 32+    for (var x = 0; x < values.length; x += 2) {
 33+      if (values[x].length < values[0].length) {
 34+        throw new CellError(ERRORS.VALUE_ERROR, "Array arguments to COUNTIFS are of different size.");
 35+      }
 36+      var criteriaEvaluation = criteriaEvaluationFunctions[x+1];
 37+      if (otherCriteriaEvaluationSuccessfulSoFar) {
 38+        if (!criteriaEvaluation(values[x][i])) { // evaluate THIS value with x+1 index, which is criteria
 39+          otherCriteriaEvaluationSuccessfulSoFar = false;
 40+        }
 41+      }
 42+    }
 43+    if (otherCriteriaEvaluationSuccessfulSoFar) {
 44+      count++;
 45+    }
 46+  }
 47+  return count;
 48+};
 49+
 50 /**
 51  * Returns the a count of the number of values in a dataset.
 52  * @param values The values or ranges to consider when counting.
 53diff --git a/src/RawFormulas/Utils.ts b/src/RawFormulas/Utils.ts
 54index ae37b82..9ca22da 100644
 55--- a/src/RawFormulas/Utils.ts
 56+++ b/src/RawFormulas/Utils.ts
 57@@ -231,7 +231,7 @@ class CriteriaFunctionFactory {
 58       return false;
 59     };
 60 
 61-    if (typeof criteria === "number") {
 62+    if (typeof criteria === "number" || typeof criteria === "boolean") {
 63       criteriaEvaluation = function (x) : boolean {
 64         return x === criteria;
 65       };
 66diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
 67index 162ab72..c0cffcc 100644
 68--- a/tests/FormulasTest.ts
 69+++ b/tests/FormulasTest.ts
 70@@ -475,7 +475,41 @@ catchAndAssertEquals(function() {
 71 }, ERRORS.NA_ERROR);
 72 
 73 
 74-assertEquals(COUNTIFS([1, 5, 10], ">4", [1, 5, 10], ">4"), 2);
 75+// Test COUNTIFS
 76+// All COUNTIF tests should also work on COUNTIFS
 77+assertEquals(COUNTIFS([1, 5, 10], ">4"), 2);
 78+assertEquals(COUNTIFS([1, 2, 2, 2, 2, 2, 2, 2], ">1"), 7);
 79+assertEquals(COUNTIFS([1, 5, 10], 5), 1);
 80+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], 5), 4);
 81+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], 10), 1);
 82+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], ">5"), 1);
 83+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "=5"), 4);
 84+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "=10"), 1);
 85+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "=     10  "), 1);
 86+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], ">0"), 6);
 87+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], ">=5"), 5);
 88+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "<10"), 5);
 89+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5, 44], "<=10"), 6);
 90+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], ">4.99"), 5);
 91+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "<4.99"), 1);
 92+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "=     1.0.0  "), 0);
 93+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "=>5"), 0);
 94+assertEquals(COUNTIFS([1, 5, 5, 5, 10, 5], "==5"), 0);
 95+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "boom"], "*o*"), 3);
 96+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "mom"], "mom"), 2);
 97+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "mom"], "?o?"), 3);
 98+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "mom"], "???"), 5);
 99+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "mom"], "????"), 0);
100+assertEquals(COUNTIFS(["mom", "pop", "dad", "etc", "mom"], "?"), 0);
101+// Now actually test COUNTIFS
102+assertEquals(COUNTIFS([1, 5, 10, 20], ">4", [0, 0, 1, 1], "=1"), 2);
103+assertEquals(COUNTIFS([1, 5, 10, 20], ">4", [0, 0, 1, 1], "=1"), 2);
104+assertEquals(COUNTIFS([1, 5, 10, 20], ">4", [0, 0, 1, 1], "=1", [0, 0, 1, 1], "=1"), 2);
105+assertEquals(COUNTIFS([1, 5, 10, 20, 40], ">4", [0, 0, 1, 1, 1], "=1", [0, 0, 0, 0, 0], "=1"), 0);
106+assertEquals(COUNTIFS([1, 2, 3, 4], ">3", [true, true, false, true], true), 1);
107+catchAndAssertEquals(function() {
108+  COUNTIFS([1, 5, 10, 20], ">4", [0, 0], "=1");
109+}, ERRORS.VALUE_ERROR);
110 
111 assertEquals(COUNTUNIQUE([1, 1, 10]), 2);
112