ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

javascriptでpartition 関数を実装

こんにちは

javascripthaskellでいうところのpartition関数を実装してみたのでメモします。

partition関数

全然一般的ではないので説明です。僕がpartition関数って言っているのは、「配列を、その要素に対して真偽を判定する関数により、真なものと偽なものの2つのグループに分ける関数」です。

たとえば以下のように動くものです。

[0,1,2,3,4,5,6,7,8,9].partition(num => num > 4)
// => [[5,6,7,8,9],[0,1,2,3,4]]

[1,"2",3].partition(item => typeof item === 'string')
// => [["2"],[1,3]]

いかにもありそうな関数ですが、探した限りではありませんでした。(altjsでは実装されている場合があるようです)

やっちゃだめみたいですがArrayのprototypeを汚染して関数を定義します。Arrayの他のコールバックを引数に取る関数を参考にしました。

Array.prototype.partition = function(callback, thisArg) {

  if (this === null) {
    throw new TypeError('Array.prototype.partition called on null or undefined');
  }

  if (typeof callback !== 'function') {
    throw new TypeError('callback must be a function');
  }

  var T;
  if (thisArg) {
    T = thisArg;
  }

  var list = Object(this);
  var length = list.length >>> 0;
  var value;

  var accepted = new Array();
  var rejected = new Array();

  for(var i = 0; i < length; i++) {
    value = list[i];
    if (callback.call(T, value, i, list)) {
      accepted.push(value);
    } else {
      rejected.push(value);
    }
  }

  return [accepted, rejected];
};

[0,1,2,3,4,5,6,7,8,9].partition(num => num > 4)
// => [[5,6,7,8,9],[0,1,2,3,4]]

[1,"2",3].partition(item => typeof item === 'string')
// => [["2"],[1,3]]

第二引数のthisArgArray.prototype.mapの仕様にあったため追記しました。使い道があまりわかりませんが例えば繰り返しの中で副作用を起こさせることができます(ちょっと意味のない例。。。)。

追記(2017/11/29): よく考えるとthisArgは関数呼ぶ時に呼び元のthisを渡したりする時に使うためでした。

[1,2,3,4,5].partition(function(num) {
  this.log(num) ;
  return num > 2;
}, {
  log(num) { console.log(`${num}を振り分けました`) } 
});

最後にreduceを使って、配列とコールバックを引数に取る関数として書いてみます。

const partition = (list, callback) => {
  if (list === null) throw new TypeError('partition called on null or undefined');
  if (typeof callback !== 'function') throw new TypeError('callback must be a function');

  return list.reduce((state, item) => {
    if(callback(item)) {
      state[0].push(item);
    } else {
      state[1].push(item);
    }
    return state;
  }, [[],[]]);
};

partition([0,1,2,3,4,5,6,7,8,9], num => num > 4)
// => [[5,6,7,8,9],[0,1,2,3,4]]

partition([1,"2",3], item => typeof item === 'string')
// => [["2"],[1,3]]

感想

テストとか書いてしっかり確かめたいですね。小さいものは書いていて楽しいです。