JavaScriptで関数型プログラミングを強力に後押しするUnderscore.jsのおすすめメソッド12選(lodashもあるよ)

  • 817
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

この記事では、関数型プログラミングを強力に後押しするライブラリ、
Underscore.jsとlodashを紹介します。

両ライブラリとも_で使用できます。
ほかのライブラリにも依存せず、
現在フロントでもサーバでもかなり人気なライブラリです。

個人的にもこれなしでは。。
といった必須なライブラリとなっています。

また、Backbone.jsといった人気なフレームワークが、
Underscoreに依存していたりします。

  • Underscore.js

GitHub: https://github.com/jashkenas/underscore
Document: http://underscorejs.org/
和訳: https://github.com/enja-oss/Underscore

  • lodash

GitHub: https://github.com/lodash/lodash
Document: http://lodash.com/docs

関数型プログラミングとは?

関数型プログラミングでは、関数でプログラムを組みます。
特徴としては下記項目などがあります。

  • 変数は再代入禁止である
  • 関数は参照透過性が保たれている(副作用がない)

その他、関数型プログラミングの入門はまずこちらから。
JavaScriptで関数型プログラミングの入門

Underscore.js

Underscore.jsとは

Underscore.jsとは、JavaScriptのための便利なメソッドが詰まったライブラリです。

また、公式のWebサイトにある通り、

Prototype.jsやRubyに見られる関数型のプログラミングをサポートしています。

関数型プログラミングに欠かせないライブラリです。

おすすめメソッド12選

each(forEach)

もっとも代表的なループ処理のメソッドです。
配列、またはオブジェクトの数だけループされ、
第二引数に渡した関数が都度実行されます。

// 配列
var array = [5, 10, 15];
_.each(array, function(element, index, array) {
  console.log(element + ' : ' + index);
});

// 結果
5  : 0
10 : 1
15 : 2

// オブジェクト
var object = {
  first: 1,
  second: 2,
  third: 3
};
_.each(object, function(value, key, object) {
  console.log(value + ' : ' + key);
});

// 結果
1 : first
2 : second
3 : third

map

each同様、ループ処理のメソッドです。
第二引数で渡した関数の、
それぞれの返り値をマッピングした新しい配列が返ってきます。

// 配列
var array = [5, 10, 15];
var doubleArray = _.map(array, function(element, index, array) {
  return element * 2;
});

console.log(array); // [5, 10, 15]
console.log(doubleArray); // [10, 20, 30]

// オブジェクト
var object = {
  first: 1,
  second: 2,
  third: 3
};
var doubleArray = _.map(object, function(value, key, object) {
  return value * 2;
});
console.log(object); // {first: 1, second: 2, third: 3}
console.log(doubleArray); // [2, 4, 6]

reduce

each同様、ループ処理のメソッドです。
第三引数で渡した値が、第二引数である関数の第一引数に渡されます。
さらにその関数の返り値が次のループ処理の第一引数に渡されます。


// 関数
var array = [1, 2, 3];
var sum = _.reduce(array, function(result, value, index) {
  result += value;
  return result;
}, 0);

console.log(sum); // 6

// オブジェクト
var object = {
  first: 1,
  second: 2,
  third: 3
};
var doubleObject = _.reduce(object, function(result, value, key) {
  result[key] = value * 2;
   return result;
}, {});

console.log(object); // {first: 1, second: 2, third: 3}
console.log(doubleObject); // {first: 2, second: 4, third: 6}

find

配列の要素のうち、初めて一致するものを一つのみ返します。

var array = [1, 2, 3, 4, 5];
var result = _.find(array, function(number) {
  return number % 2 === 0;
});

console.log(result); // 2

filter

配列の要素のうち、一致するものすべてを配列として返します。

var array = [1, 2, 3, 4, 5];
var result = _.filter(array, function(number) {
  return number % 2 === 0;
});

console.log(result); // [2, 4]

contains

配列がその値をもっているか判定します。

var array = [1, 2, 3, 4, 5];

var result1 = _.contains(array, 2);
console.log(result1); // true

var result2 = _.contains(array, 10);
console.log(result2); // false

uniq

配列の重複を省きます。

var array = [1, 2, 3, 4, 1, 2];
var result = _.uniq(array);

console.log(result); // [1, 2, 3, 4]

pluck

オブジェクトが入っている配列から、
各オブジェクトの指定されたプロパティを取り出します。

var array = [
  {name: 'Haru39', age: 26},
  {name: 'yutapon', age: 18},
  {name: 'slime', age: 15}
];
var result = _.pluck(array, 'name');

console.log(result); // ["Haru39", "yutapon", "slime"]

keys

オブジェクトのキーを要素にもった配列を返します。

var object = {
  first: 1,
  second: 2,
  third: 3
};
var keys = _.keys(object);

console.log(keys); // ["first", "second", "third"]

values

オブジェクトの値を要素にもった配列を返します。

var object = {
  first: 1,
  second: 2,
  third: 3
};
var values = _.values(object);

console.log(values); // [1, 2, 3]

has

オブジェクトが指定されたプロパティを持っているかを判定します。

var object = {
  first: 1,
  second: 2,
  third: 3
};
var result = _.has(object, 'second');

console.log(result); // true

bind

第一引数に関数、第二引数にthisにsetしたいオブジェクトを渡します。
第三引数以降は、その第一引数である関数の引数となります。

var person = {
  name: 'Haru39',
  say: function(age) {
    console.log(this.name + 'は' + age);
  }
};

person.say('26歳'); // Haru39は26歳

var say = person.say;
say(); // undefinedはundefined

say = _.bind(say, person);
say('26歳'); // Haru39は26歳

say = _.bind(say, person, '26歳');
say(); // Haru39は26歳

実践向けライブラリlodash

lodashとは

lodashとは、Underscore.jsと互換性を持ったライブラリです。
下記が主な特徴となります。

  • Underscore.jsと互換性がある
  • Underscore.jsより機能がある
  • Underscore.jsより早い

パフォーマンスに関してはよほど大規模な処理をしない限り、
正直差は出にくいです。

しかし、ほかに便利な機能が実装されていることに加え、
Underscore.jsとの互換性もあるため、
実践ではほとんどの場合で、lodashを使用することになると思います。

Underscore.jsにない、lodashおすすめメソッド4選

cloneDeep

cloneはUnderscore.js同様、shallow cloning、
それとは異なりcloneDeepは、deep cloningとなります。

deepはオブジェクトの参照はコピーせず、すべての値をクローンし直しています。
よって、すべての値が同一の別のオブジェクトが生成されます。


var personList = [
  { 'name': 'Haru39', 'age': 26 },
  { 'name': 'yutapon', 'age': 18 }
];

// clone
var clone = _.clone(personList);
var result1 = clone[0] === personList[0];

console.log(result1); // true

// cloneDeep
var deep = _.cloneDeep(personList);
var result2 = deep[0] === personList[0];

console.log(result2); // false

transform

Underscore.jsのreduceの代替です。
配列なら配列を、オブジェクトならオブジェクトが第一引数にsetされます。

reduceでの処理は、transformで置き換えるとより保守性があがります。


// 配列
var array = [1, 2, 3];
var doubleArray = _.transform(array, function(result, number) {
  result.push(number * 2);
  return result;
});

console.log(array); // [1, 2, 3]
console.log(doubleArray); // [2, 4, 6]

// オブジェクト
var object = {
  first: 1,
  second: 2,
  third: 3
};
var doubleObject = _.transform(object, function(result, value, key) {
  result[key] = value * 2;
  return result;
});

console.log(object); // {first: 1, second: 2, third: 3}
console.log(doubleObject); // {first: 2, second: 4, third: 6}

curry

引数毎にカリー化された関数を作成できます。

var curried = _.curry(function(a, b, c) {
  console.log(a + b + c);
});

curried(1)(2)(3); // 6

curried(1, 2)(3); // 6

curried(1, 2, 3); // 6

merge

Underscore.jsのextendのdeep版です。


// extend
var extend = _.extend({name: 'Haru39'}, {age: 26});

console.log(extend); // {name: "Haru39", age: 26}

// merge
var names = {
  'characters': [
    { 'name': 'Haru39' },
    { 'name': 'yutapon' }
  ]
};
var ages = {
  'characters': [
    { 'age': 26 },
    { 'age': 18 }
  ]
};

var merge = _.merge(names, ages);

console.log(merge);
/**
 * [{
 *   name: 'Haru39',
 *   age: 26
 * },{
 *   name: 'yutapon',
 *   age: 18
 * }]
 */

外部アカウント

技術情報のみつぶやくアカウント作成しました。JavaScriptは最新情報も追っていきます。
Twitterはこちら
Feedlyのフォローはこちら

おすすめの記事

もういい時期です。そろそろ始めましょう。ECMAScript6。
もうはじめよう、ES6~ECMAScript6の基本構文まとめ(JavaScript)

JavaScriptでは関数はすべてクロージャ。
そもそもクロージャって?JavaScriptでクロージャ入門

JavaScriptでは関数型言語の一部の機能?実践的なJavaScriptの関数型とは。
JavaScriptで関数型プログラミングの入門