PageRenderTime 166ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/JSGohan/reduce.md

https://github.com/azu/slide
Markdown | 240 lines | 177 code | 63 blank | 0 comment | 0 complexity | 3aac55f83cc991f476e0d421cc1602b1 MD5 | raw file
  1. # Array.prototype.reduce Dance
  2. ---
  3. ## Basic
  4. > var assert = require("assert");
  5. * forと違いimmutableなオブジェクトを使わないで合計を出せる
  6. * <a href="http://toolness.github.io/slowmo-js/?code=%5B1%2C%202%5D.reduce(function%20(x%2C%20y)%20%7B%20return%20x%20%2B%20y%20%7D)%3B&amp;filterrange=5-5">Basic flow</a>
  7. ``` js
  8. var total = [1, 2, 3, 4, 5].reduce(function (a, b) {
  9. return a + b;
  10. });
  11. assert.equal(total, 15);
  12. ```
  13. ---
  14. ### 初期値
  15. * 初期値を指定することもできる
  16. ``` js
  17. var initialTotal = [0, 1, 2, 3, 4].reduce(function (a, b) {
  18. return a + b;
  19. }, 10);
  20. assert.equal(initialTotal, 20);
  21. ```
  22. ---
  23. ### 例外
  24. * 空の配列の場合は例外を吐く
  25. * [15.4.4.21 Array.prototype.reduce](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21 "15.4.4.21 Array.prototype.reduce")
  26. * Step 8.c
  27. ``` js
  28. assert.throws(
  29. function () {
  30. [].reduce(function (a, b) {
  31. return a + b
  32. });
  33. },
  34. TypeError,
  35. "k < len の場合はTypeError"
  36. );
  37. ```
  38. ----
  39. ---
  40. ## every by reduce
  41. ``` js
  42. var assert = require("assert");
  43. assert.ng = function(value, message){
  44. assert.equal(false, !!value, message);
  45. };
  46. ```
  47. * ``reduce`` があれば補完mapやeveryなどは実装できる
  48. * 柔軟性が高い
  49. > このうちreduceが一番強力でmapやfilterやsumなど他の関数もこれをもとに定義できます
  50. via [Functional JavaScript](https://gist.github.com/ympbyc/5564146 "Functional JavaScript")
  51. ---
  52. ### [Array.every](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/every "Array.every")
  53. ``` js
  54. function every(array, predicate) {
  55. return array.reduce(function (prev, current, index, list) {
  56. if (prev) {
  57. return predicate(current, index, list);
  58. } else {
  59. return prev;
  60. }
  61. }, true);
  62. }
  63. function isBigEnough(element, index, list) {
  64. return (element >= 10);
  65. }
  66. assert.ok(every([12, 130, 44], isBigEnough));
  67. assert.ng(every([1, 100, 200], isBigEnough));
  68. ```
  69. ----
  70. ## map
  71. > var _ = require("underscore");
  72. * ``map`` は基本的に入力の個数と出力の個数が同じ
  73. * ``map`` だけだと ``filter`` (select) までは行えない
  74. ``` js
  75. var filteredNumberToString = function (e) {
  76. if (typeof e === "number") {
  77. return String(e);
  78. }
  79. };
  80. var mappedArray = [1, null, 3].map(filteredNumberToString);
  81. // 現実はundefinedになる
  82. assert.deepEqual(mappedArray, ["1", undefined, "3"]);
  83. ```
  84. ---
  85. ### flatMap
  86. * ``reduce`` なら違う形(入力と出力の個数が異なる)ものを返せる
  87. ``` js
  88. function flatMap(obj, iterator) {
  89. return _.reduce(obj, function (memo, value, index, list) {
  90. var items = iterator(value, index, list);
  91. if (items == null) {
  92. return memo;
  93. }
  94. return memo.concat(items);
  95. }, []);
  96. }
  97. var flatMappedArray = flatMap(["string", 1, null, 3], filteredNumberToString);
  98. // Numberだけにfilter + NumberをStringに変換した結果を返す
  99. assert.deepEqual(flatMappedArray, ["1", "3"]);
  100. ```
  101. ----
  102. ### 高階関数
  103. * 関数を返す関数の事
  104. ``` js
  105. var ComparisonResult = {
  106. ascending: -1,// <
  107. same: 0, // ==
  108. descending: 1 // >
  109. };
  110. function comparator(predicate) {
  111. return function (x, y) {
  112. if (predicate(x, y)) {
  113. return ComparisonResult.ascending
  114. } else if (predicate(y, x)) {
  115. return ComparisonResult.descending;
  116. }
  117. return ComparisonResult.same;
  118. };
  119. }
  120. ```
  121. ---
  122. ### Predicates to ComparisonResult
  123. * 真偽値 -> comparator を通して -> ``ComparisonResult`` を返す関数を作る
  124. * 真偽値を返す関数を Predicates という
  125. * 真偽値から ``<`` ``==`` ``>`` の3種類の状態を返せるのでシンプル
  126. ``` js
  127. function isLessOrEqual(x, y) {
  128. return x <= y;
  129. }
  130. var values = [2, 3, -1, -6, 0, -108, 42, 10];
  131. var expectedSortedValues = [-108, -6, -1, 0, 2, 3, 10, 42];
  132. var results = values.sort(comparator(isLessOrEqual));
  133. assert.deepEqual(results, expectedSortedValues);
  134. ```
  135. ----
  136. ---
  137. ## Null Guard
  138. * 配列に **falsy** な値が含まれてる意図しない結果になってしまう
  139. ``` js
  140. var nums = [1, 2, 3, null, 5];
  141. var multFn = function (total, n) {
  142. return total * n;
  143. };
  144. _.reduce(nums, multFn);// => 0 になってしまう…
  145. ```
  146. ---
  147. ### Null Check?
  148. * multFnにnullチェックを入れるのは本質的じゃない
  149. * nullチェックを加える **高階関数** を作る
  150. ``` js
  151. function fnull(fn, defaultValue) {
  152. return function () {
  153. // falsyだった場合はdefaultValueにしたものに引数を構築し直す
  154. var args = _.map(arguments, function (e) {
  155. return e != null ? e : defaultValue;
  156. });
  157. return fn.apply(null, args);
  158. }
  159. }
  160. ```
  161. ---
  162. ### safeMult
  163. * falsyな値はdefalutValueに変更される
  164. ``` js
  165. // var nums = [1, 2, 3, null, 5];
  166. // falsyな値はdefalutValue(1)に変更される
  167. var safeMultFn = fnull(multFn, 1);
  168. var totalMult = _.reduce(nums, safeMultFn);
  169. assert.equal(totalMult, 30);
  170. ```
  171. ---
  172. ### おわり
  173. サンプルコード
  174. * [azu/ReduceDance](https://github.com/azu/ReduceDance "azu/ReduceDance")
  175. 参考
  176. * [functional javascript](http://www.functionaljavascript.com/ "functional javascript")
  177. * [reduce関数は結構有用っていうお話 - あと味](http://taiju.hatenablog.com/entry/20110331/1301535208#fn1 "reduce関数は結構有用っていうお話 - あと味")