Enumerableクラス(3)

injectメソッド

【抜粋】
  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

第一引数memoを初期値として、iteratorによりmemoを変更していき、最終的にこれを返却しています。memoは配列やカスタムオブジェクトの他、文字列や数値も設定できます。

【例】
var array = ["one", "two", "three"];
var ret = array.inject(["zero"],
function(memo, value, index){
  memo.push(value);
  memo.push(value);
  return memo;
});
alert(ret);
//retは配列。"zero,one,one,two,two,three,three"と表示される。

invokeメソッド

【抜粋】
  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.collect(function(value) {
      return value[method].apply(value, args);
    });
  },

前述collectメソッドが使用されています。collectメソッドはthisの各要素の値を引数の関数で処理し、その結果を配列にまとめて返すものです。collectメソッドへの引数の関数は、各要素の値(この場合オブジェクト)の第一引数methodと同名のメソッドを実行します。apply関数によりinvokeの第二引数以降を、value[method]の引数としています。

【例】
var dog = {
  sing : function(f, b){return f + "わん" + b;}
}
var cat = {
  sing : function(f, b){return f + "にゃー" + b;}
}
var cock = {
  sing : function(f, b){return f + "こけこっこー" + b;}
}
var array = [dog, cat, cock];
var ret = array.invoke("sing", "「", "」");
alert(ret);
//retは配列。"「わん」,「にゃー」,「こけこっこー」"と表示される。

maxメソッド

【抜粋】
  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value >= (result || value))
        result = value;
    });
    return result;
  },

iteratorの処理結果の値が、それまでの最大値resultよりも大きい場合に、resultと置き換えています。結果として最大値が返却されます。iteratorがnull(undefined)の場合は、第一引数をそのまま返却するPrototype.Kメソッドが使用されます。「value >= (result || value)」は、resultの初期値が未定義(undefined)なので、ループの初回で必ずtrueとなります。

【例】
var array = ["zero", "one", "two", "three"];
var ret = array.max(function(value, index){
  return value.length - index;
});
alert(ret);
//"4"と表示される。"zero"の文字数-0の結果。

minメソッド

【抜粋】
  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value <= (result || value))
        result = value;
    });
    return result;
  },

前述maxメソッドの逆ですね。

【例】
var array = ["zero", "one", "two", "three"];
var ret = array.min(function(value, index){
  return value.length - index;
});
alert(ret);
//"1"と表示される。"two"の文字数-2の結果。

pluckメソッド

【抜粋】
  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

各要素の値(この場合オブジェクト)について、引数propertyと同名のプロパティを配列に格納して返却しています。

【例】
var array = ["zero", "one", "two", "three"];
var ret = array.pluck("length");
alert(ret);
//retは配列。"4,3,3,5"が表示される。

rejectメソッド

【抜粋】
  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

iteratorがfalseを返却した場合、そのときの要素の値すべてを配列に格納して返却します。前述findAllメソッドと逆の機能です。

【例】
var array = ["zero", "one", "two", "three"];
var ret = array.reject(function(value, index){
  return (value.length == 3); //lengthが3の場合trueを返却
});
alert(ret);
//retは配列。"zero,three"が表示される。

sortByメソッド

【抜粋】
  sortBy: function(iterator) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

まず、各要素の値をvalueiteratorの処理結果をcriteriaという名前のプロパティで持つ、オブジェクトの配列を作成しています。次に、これを既存のsortメソッドでソートしています。ソートは各オブジェクトのcriteriaプロパティを元に行います。*1ソート関数は正数を返却すると隣り合う要素が入れ替わりますので、この場合、昇順ソートになります。最後に前述pluckメソッドを使い、valueプロパティのみを配列に格納して返却しています。

【例】
var array = ["zero", "one", "two", "three"];
var ret = array.sortBy(function(value, index){
  return value.length;
});
alert(ret);
//retは配列。"one,two,zero,three"が表示される。

つまりこれはオブジェクト配列が特定のプロパティでソートできるということです。

以下は2次元配列のソート例です。

var array = [[0, 10], [1, 9], [2, 8], [3, 7]];
var ret = array.sortBy(function(value, index){
  return value[1];//2列目でソート
});
alert(Object.inspect(ret));
//"[[3, 7], [2, 8], [1, 9], [0, 10]]"と表示される。

以下はHashオブジェクト(連想配列)のソート例です。返却されるのはキーと値が対になっている2次元配列なので、再度Hashに変換する必要があります。(なんだかな・・・)→Hashに変換すると、、eachメソッドやfor inループで処理される順番が崩れる場合があります。具体的にはOperaでキーに自然数がある場合、これが優先的に処理されてしまいます。Hashオブジェクトのソート結果が配列であるのは妥当ということかもしれません。

//注:Operaでキーに自然数が含まれると正常に動作しないです。以下は参考程度に。
var hash = $H({'one':'1', 'two':'02', 'three':'003'});
var ret = hash.sortBy(function(prop, index){
  return prop.key;
}).inject($H(), function(memo, value, index){
  memo[value[0]] = value[1];
  return memo;
});
alert(Object.inspect(ret));
//"#<Hash:{'one': '1', 'three': '003', 'two': '02'}>"と表示される。

prop.keyをprop.valueにすれば値でのソートになります。また、Number(prop.value)とすれば数値としてソートします。-(Number(prop.value))とすれば数値として降順ソートになります。文字辞書順の降順は簡単には書けなそうです。(sortBy後にreverseしてHashにするしかないような・・・)

修正案

文字列降順にも対応させると便利な気がします。

【参考】修正案
  sortBy: function(iterator, isDesc) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      if(!isDesc){
        return a < b ? -1 : a > b ? 1 : 0;
      }else{
        return a > b ? -1 : a < b ? 1 : 0;
      }
    }).pluck('value');
  },

第二引数にtrueを設定すると降順になります。

toArrayメソッド

【抜粋】
  toArray: function() {
    return this.collect(Prototype.K);
  },

各要素の値を配列に格納して返却します。後述のHashオブジェクトをArrayオブジェクトに変換する場合や、Arrayクラスオブジェクトの複製を作成するのに有効です。

var hash = $H({zero:"零",one:"壱",two:"弐",three:"参"});
var array = hash.toArray();
alert(array[2][0]); //"two"と表示される
alert(array[2][1]); //"弐"と表示される
alert(array.two);//"undefined"と表示される

*1:余談ですが、このソート用関数の作成方法は有用だと思います。2次元配列を特定のカラムでソートすることも可能になります。