underscore.jsとは?
配列・オブジェクトの操作(検索、演算、列挙など)をよりシンプルに記述するためのライブラリです。
HTMLのscriptタグで読み込みするだけですぐに使うことができます。
まず読み込んでみよう
2014/02現在の最新版は1.6.0です。
- 公式サイトを開きます。
- ダウンロードする人は「Donwloads」を、bowerなどでインストールする人は「Installation」を参照してください。
- HTMLにscriptタグを書きます。 br>underscore.jsまたはunderscore-min.jsを読み込みします。
- 準備OK。
何が便利?
例えばこんな配列があったとします。
member = [ {id:1, name:'yamada', age:15, gender:'male', score:30, num:[11,12,21,10]}, {id:2, name:'suzuki', age:46, gender:'female', score:55, num:[85,11,4,30]}, {id:3, name:'yasuda', age:38, gender:'male', score:45, num:[56,32,17,90]} ];
そして年齢が20以下と以上で配列を分けたいとします。
普通にループで書くとこのようになりますね。
var under20 = [];
var over20 = [];
var i;
for (i in member) {
if (member[i].age < 20) {
under20.push(member[i]);
} else {
over20.push(member[i]);
}
}
under20 -> [{id:1, name:'yasuda'...}]
over20 -> [{id:2, name:'suzuki'...}, {id:3, name:'yasuda'...]
これをunderscore.jsを使って書くと…
var results = _.groupBy(member, function(item) {
return item.age < 20;
});
results ->
[
true: [{id:1, name:'yasuda'...}]
false: [{id:2, name:'suzuki'...}, {id:3, name:'yasuda'...]
]
短い!
変数が少ない!
ループの処理の検証もしなくて良い!
これは便利ですね。
いろんな関数があるみたいですが書き方が一緒で覚えやすい。
公式サイトの「Collection」にあるデータ収集関連の関数はだいたい書き方が一緒です。
まずアンダースコアを使って呼び出します。
_.groupBy()
そして引数にはデータとイテレータを渡します。
_.groupBy(data, function(){})
イテレータの引数に適当な変数名をつけると、処理中のオブジェクトにアクセスできるようになります。
_.groupBy(data, function(item){})
イテレータの内容は関数によって違います。
groupBy()の場合はグループ化のキーを返すようにしておきます。
_.groupBy(data, function(item){ item.age < 20; })
これを実行するとtrueまたはfalseでグループ化されたオブジェクトが返されます。
false: [ {id: 1, name: 'yamada', age: 15}, {id: 2, name: 'suzuki', age: 46} ], true: [ {id: 3, name: 'yasuda', age: 38} ]
いろいろ使ってみよう
公式サイトでは配列を使ったサンプルがたくさん紹介されています。
実際には単純な配列よりオブジェクトの配列のほうが使う機会が多いので、先ほどのmember配列をつかって試してみます。
member = [ {id:1, name:'yamada', age:15, gender:'male', score:30, num:[11,12,21,10]}, {id:2, name:'suzuki', age:46, gender:'female', score:55, num:[85,11,4,30]}, {id:3, name:'yasuda', age:38, gender:'male', score:45, num:[56,32,17,90]} ];
使い方がちょっと...みたいな例もありますがご了承ください。ざっと読んでもらえると「こんなことができるんだ」というイメージを掴んでもらえるかと思います。
each
繰り返し処理をする
var result = 0;
_.each(member, function(item, i){
result += item.score;
});
-> 130
map
配列を取り出す
var result = _.map(member, function(item, i){
return item.id;
});
->[1,2,3]
reduce
合計
var map = _.map(member, function(item){ return item.score; });
var result = _.reduce(map, function(num1, num2){
return num1 + num2;
});
->130
find
検索、一致したらストップ
var result = _.find(member, function(item){
return item.age > 20;
});
->{"id":2,"name":"suzuki","age":46,"gender"...}
filter
検索、条件に合うものをすべて返す
var result = _.filter(member, function(item){
return item.age > 20;
});
->[
{"id":2,"name":"suzuki","age":46,"gender"...},
{"id":3,"name":"yasuda","age":38,"gender"...}
]
findWhere
検索(オブジェクトで指定)、一致したらストップ
var result = _.findWhere(data, { gender:'male' });
->{"id":1,"name":"yamada","age":15,"gender"...}
filter
検索(オブジェクトで指定)、条件に合うものをすべて返す
var result = _.where(data, { gender:'male' });
->[
{"id":1,"name":"yamada","age":15,"gender"...},
{"id":3,"name":"yasuda","age":38,"gender"...}
]
reject
一致しないものを返す
var result = _.reject(member, function(item){
return item.age == 15;
});
->[
{"id":2,"name":"suzuki","age":46,"gender":...},
{"id":3,"name":"yasuda","age":38,"gender":...}
]
every
すべて真かどうか
var score = _.map(data, function(item){ return item.score; });
return _.every(score, _.identity);
->true
some
真が含まれるか
var score = _.map(data, function(item){ return item.score; });
return _.some(score, _.identity);
->true
contains
値が含まれるか
var lists = _.map(data, function(item){ return item.age; });
return _.contains(lists, 46);
->true
pluck
プロパティを配列に
return _.pluck(data, 'name');
->['yamada','suzuki','yasuda']
sortBy
ソート
return _.sortBy(data, function(item){
return item.age;
});
->[
{id:1, name:'yamada', age:15, gender:...},
{id:3, name:'yasuda', age:38, gender:...},
{id:2, name:'suzuki', age:46, gender:...}
]
min
最小値
return _.min(member, function(item){
return _.min(item.num, function(n){
return n;
});
});
->{id:2, name:'suzuki', age:46, gender:...}
max
最小値
return _.max(data, function(item){
return _.max(item.num, function(n){
return n;
});
});
->{id:2, name:'yasuda', age:38, gender:...}
count
カウント
var result = [];
_.each(member, function(item){
// 条件に対するカウントがtrue/falseの配列で返るのでtrueだけ取り出す
result.push(
_.countBy(item.num, function(n){
return n > 30;
})[true]
);
});
->[null,1,3]
shuffle
シャッフル
var result = _.shuffle(data);
->[
{id:2, name:'suzuki', age:46, gender:...},
{id:1, name:'yamada', age:15, gender:...},
{id:3, name:'yasuda', age:38, gender:...}
]
size
数
return _.size(data);
->3
keys
キーを列挙
return _.keys(data[0]);
->['id','name','age','gender','score','num']
values
値を列挙
return _.values(data[0]);
->[1,'yamada',15,'male',30,11,12,21,10]
pairs
キーと値のペア
return _.pairs(member[0]);
->[
["id",1],
["name","yamada"],
["age",15],
["gender","male"],
["score",30],
["num",[11,12,21,10]]
]
invert
キーと値を入れ替える
return _.invert(data[0]);
->{
"1":"id",
"15":"age",
"30":"score",
"yamada":"name",
"male":"gender",
"11,12,21,10":"num"
}
extend
オブジェクトの追加
var result = _.extend(data[0], { section_id:1 }, { area_id:1 });
->{
"id":1,
"name":"yamada",
"age":15,
"gender":"male",
"score":30,
"num":[11,12,21,10],
"section_id":1,
"area_id":1
}
omit
オブジェクトの削除
var result = _.omit(data[0], 'gender', 'age', 'num');
->{
"id":1,
"name":"yamada",
"score":30
}
has
オブジェクトが存在するか
var result = _.has(data[0], 'age');
->true
まとめ
長いことサンプルを書いてみましたが、他にも便利そうな関数がたくさんあります。
underscore.jsを使ったら、いちいち作っていた「idを検索する」「特定のキーでソートする」みたいな関数が減ってとても便利になりそうな気がします。