Repository: calmm-js/partial.lenses
Pull request #61 (2017-03-11 15:28:45)

Added `count` fold

codecov-io: # [Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/61?src=pr&el=h1) Report
> Merging [#61](https://codecov.io/gh/calmm-js/partial.lenses/pull/61?src=pr&el=desc) into [master](https://codecov.io/gh/calmm-js/partial.lenses/commit/cfac88d3ad87cd21964acc0ad87f69a6a639eb80?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.


```diff
@@          Coverage Diff          @@
##           master    #61   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           1      1           
  Lines         434    436    +2     
=====================================
+ Hits          434    436    +2
```


| [Impacted Files](https://codecov.io/gh/calmm-js/partial.lenses/pull/61?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/partial.lenses.js](https://codecov.io/gh/calmm-js/partial.lenses/compare/cfac88d3ad87cd21964acc0ad87f69a6a639eb80...fe3c14eae365edb698075ac2ef382524ed77dcc8?src=pr&el=tree#diff-c3JjL3BhcnRpYWwubGVuc2VzLmpz) | `100% <100%> (ø)` | :white_check_mark: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/61?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/61?src=pr&el=footer). Last update [cfac88d...fe3c14e](https://codecov.io/gh/calmm-js/partial.lenses/compare/cfac88d3ad87cd21964acc0ad87f69a6a639eb80...fe3c14eae365edb698075ac2ef382524ed77dcc8?src=pr&el=footer&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Patch:
diff --git a/README.md b/README.md
index b526808..c71a08a 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,7 @@ parts.  [▶ Try Lenses!](https://calmm-js.github.io/partial.lenses/)
       * [`L.any((maybeValue, index) => testable, traversal, maybeData) ~> boolean`](#L-any "L.any: ((Maybe a, Index) -> Boolean) -> PTraversal s a -> Boolean")
       * [`L.collect(traversal, maybeData) ~> [...values]`](#L-collect "L.collect: PTraversal s a -> Maybe s -> [a]")
       * [`L.collectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> [...values]`](#L-collectAs "L.collectAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> [b]")
+      * [`L.count(traversal, maybeData) ~> number`](#L-count "L.count: PTraversal s a -> Number")
       * [`L.first(traversal, maybeData) ~> maybeValue`](#L-first "L.first: PTraversal s a -> Maybe s -> Maybe a")
       * [`L.firstAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-firstAs "L.firstAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b")
       * [`L.foldl((value, maybeValue, index) => value, value, traversal, maybeData) ~> value`](#L-foldl "L.foldl: ((r, Maybe a, Index) -> r) -> r -> PTraversal s a -> Maybe s -> r")
@@ -1264,6 +1265,18 @@ L.concatAs(R.pipe(R.negate, toCollect),
 The internal implementation of `L.collectAs` is optimized and faster than the
 above naïve implementation.
 
+##### <a id="L-count"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/#L-count) [`L.count(traversal, maybeData) ~> number`](#L-count "L.count: PTraversal s a -> Number")
+
+`L.count` goes through all the elements focused on by the traversal and counts
+the number of non-`undefined` elements.
+
+For example:
+
+```js
+L.count([L.elems, "x"], [{x: 11}, {y: 12}])
+// 1
+```
+
 ##### <a id="L-first"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/#L-first) [`L.first(traversal, maybeData) ~> maybeValue`](#L-first "L.first: PTraversal s a -> Maybe s -> Maybe a")
 
 `L.first` goes lazily over the elements focused on by the given traversal and
diff --git a/src/partial.lenses.js b/src/partial.lenses.js
index 179a98b..d7f07af 100644
--- a/src/partial.lenses.js
+++ b/src/partial.lenses.js
@@ -71,6 +71,7 @@ function ConcatOf(ap, empty, delay) {
 }
 
 const Monoid = (concat, empty) => ({concat, empty: () => empty})
+const Sum = Monoid((y, x) => x + y, 0)
 
 const Mum = ord =>
   Monoid((y, x) => void 0 !== x && (void 0 === y || ord(x, y)) ? x : y)
@@ -602,6 +603,8 @@ export const collectAs = curry((xi2y, t, s) =>
 
 export const collect = collectAs(id)
 
+export const count = concatAs(x => void 0 !== x ? 1 : 0, Sum)
+
 export const firstAs = curry(mkFirst(x => void 0 !== x ? the(x) : x))
 
 export const first = firstAs(id)
@@ -626,7 +629,7 @@ export const or = any(id)
 
 export const product = concatAs(unto(1), Monoid((y, x) => x * y, 1))
 
-export const sum = concatAs(unto(0), Monoid((y, x) => x + y, 0))
+export const sum = concatAs(unto(0), Sum)
 
 // Creating new traversals
 


Test patch:
diff --git a/test/core.js b/test/core.js
index 5f8fa71..e409550 100644
--- a/test/core.js
+++ b/test/core.js
@@ -26,6 +26,8 @@ const concatDefined = m => R.curry((t, s) => concat(m, [t, optional], s))
 
 const toPartial = f => x => isDefined(x) ? f(x) : x
 
+const Sum = {empty: () => 0, concat: R.add}
+
 // Optics
 
 export const toFunction = L.toFunction
@@ -92,13 +94,15 @@ export const collect = R.curry((t, s) => collectAs(R.identity, t, s))
 export const collectAs = R.curry((to, t, s) =>
   concatAs(R.pipe(to, toCollect), Collect, t, s))
 
+export const count = concatAs(x => x !== undefined ? 1 : 0, Sum)
+
 export const maximum = concat({empty: () => {}, concat: maxPartial})
 export const minimum = concat({empty: () => {}, concat: minPartial})
 
 export const or = any(R.identity)
 
 export const product = concatDefined({empty: () => 1, concat: R.multiply})
-export const sum = concatDefined({empty: () => 0, concat: R.add})
+export const sum = concatDefined(Sum)
 
 // Creating new traversals
 
diff --git a/test/tests.js b/test/tests.js
index 14339cb..40cd643 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -159,6 +159,7 @@ describe("arities", () => {
     compose: 0,
     concat: 3,
     concatAs: 4,
+    count: 2,
     defaults: 1,
     define: 1,
     elems: 4,
@@ -599,6 +600,8 @@ describe("folds", () => {
          [[[0,3],2],1])
   testEq(`L.foldl((x,y) => [x,y], 0, [L.elems, L.elems], [[1,2],[3]])`,
          [[[0,1],2],3])
+  testEq(`L.count([L.elems, L.choice("x","y")], [{x:11}, {z:33}, {y:22}])`, 2)
+  testEq(`L.count(flatten, [[],{},[[[],[{x:[],y:[]}],{}]]])`, 0)
   ;[`foldl`, `foldr`].forEach(fold => {
     testEq(`X.${fold}((x,y) => x+y, 0, X.elems, a100000)`,
            100000)
diff --git a/test/types.js b/test/types.js
index 5469e99..272b1ad 100644
--- a/test/types.js
+++ b/test/types.js
@@ -122,6 +122,8 @@ export const collectAs = T.fn([T.fn([T_maybeData, T_index], T_maybeData),
                                T_maybeData],
                               T.array(T_data))
 
+export const count = T.fn([T_traversal, T_maybeData], T.number)
+
 export const firstAs =
   T.fn([T.fn([T_maybeData, T_index], T.any),
         T_traversal,
@@ -142,8 +144,8 @@ export const minimum = T.fn([T_traversal, T_maybeData], T.any)
 
 export const or = T.fn([T_traversal, T_maybeData], T.boolean)
 
-export const product = T.fn([T_traversal, T_maybeData], T.number)
-export const sum = product
+export const product = count
+export const sum = count
 
 // Creating new traversals