ひよっこPGのブログ

主に、技術メモや英語たまにギター関連のことも書いているブログです。

UnderScore.js の _.reduceメソッドに焦点をあてて勉強してみる

この記事は、タイトルどおり UnderScore.jsの _.reduceメソッドにカーソルをあてて書いている記事です。
ターゲットとしては、 Underscore.js reduce などで検索して訪問された方を対象にしています。


記事の書く流れ

1. _.reduceメソッドの使い方と概要
てっとり早く使い方を知りたいという方は、ここを読んでください。

2. 内部でどのように動いているか詳細を説明
内部の動きを知ることによって、reduceメソッドの仕様を説明していきます。
ご興味のある方は、どうぞ。

UnderScore.jsについて

JavaScriptのライブラリの一つで、これをインポートすると
_ オブジェクトがグローバル変数に設定されて,その _ オブジェクト内に
便利関数が80ほど定義されている。

公式サイト
http://underscorejs.org/
UnderScore.js ダウンロード先
http://underscorejs.org/underscore.js

//インポート例
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>

_.reduceメソッドの使い方と概要

基本的な使い方は、配列の要素を1個ずつ取り出して まとめたいという時に使います。
では、配列を1個ずつ取り出して、一つの文字列にする使い方を書いてみますね。

var array = ["a", "b", "c"];

var iterator = function(sum, element){
    sum += element; 
    return sum;
}
var result = _.reduce(array, iterator);
console.log(result);
//=> "abc"

上記のように _.reduceメソッドを使って 配列要素を一つの文字列に出来ます。

使い方ですがまず、reduceメソッドの引数について説明します。
_.reduce( list, iterator, [memo], [context])

list 処理したい配列やオブジェクト
iterator 配列要素、個々の処理を担当する関数
memo 省略可 まとめる時の初期値
context 省略可 iterator関数内のthisに設定するオブジェクト

iterator関数の引数は下記になります。
iterator( sum, element, [index], [list])

sum 累積変数 まとめ先の変数
element 配列を1個ずつ取り出した値
index 省略可 処理中の配列要素番号やkey名
list 省略可 処理中の配列やオブジェクト

なお iterator関数は、処理結果の値を返す必要があります。


なんとなくイメージはお分かりになれましたでしょうか?
もう一つサンプルと memo まとめる時の初期値を 省略した場合の注意点を書いて 概要の説明は終わります。

var array = [1, 2, 3];
var result = _.reduce(array, function(result, value){result +=value; return result;});
console.log(result);
//=> 6

var array = [1, 2, "3"];
var result2 = _.reduce(array, function(result, value){result +=value; return result;});
console.log(result2);
//=> 33

1個目は、配列内の数値を合計する使い方です。

2個目は、同じように処理されていますが途中 文字列が含まれています。
その場合 (1+2)+"3" = 33 となることにご注意ください。
memo まとめる時の初期値を省略した場合は、 配列やオブジェクトの一番最初の要素が初期値になります。

内部でどのように動いているか詳細を説明

まず、UnderScore.js内で、reduceメソッドを定義している部分をはりつけます。

  var reduceError = 'Reduce of empty array with no initial value';

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    var initial = arguments.length > 2;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError(reduceError);
    return memo;
  };

まず結論から先にいうと
渡されたlist が 配列の場合で
Array.prototype.reduce - JavaScript | MDN
Array.prototype.reduceメソッドに対応しているブラウザであればその関数を使う。

Array.prototype.reduceメソッドに対応していないブラウザの場合は
Array.forEach - JavaScript | MDN
Array.prototype.forEachメソッドを使う。

Array.prototype.forEachメソッドに対応していない
or
配列以外の場合は

UnderScore.jsで独自に定義されている 処理を使う。


かんたんにいうと・・・。
nativeメソッドに対応していれば、nativeメソッドを使い
対応していなければ、独自に定義した関数を使う。


記事が長くなるので、次記事でコードの説明をしてみようと思います。