読者です 読者をやめる 読者になる 読者になる

Underscore.jsおさらい5(Objects)

JavaScript Underscore.js
keys _.keys(object)

keyの一覧が配列で返る。

_.keys({one: 1, two: 2, three: 3});
=> ["one", "two", "three"]


allKeys _.allKeys(object)

prototypeも含めて全キーが返る。

function Stooge(name) {
  this.name = name;
}
Stooge.prototype.silly = true;
_.allKeys(new Stooge("Moe"));
=> ["name", "silly"]


values _.values(object)

valueの一覧が配列で返る。key()と同様、prototypeは含まれない。

_.values({one: 1, two: 2, three: 3});
=> [1, 2, 3]


mapObject _.mapObject(object, iteratee, [context])

iteratee処理結果の新しいオブジェクトができる。
Collectionsのmapのオブジェクト版。

_.mapObject({start: 5, end: 12}, function(val, key) {
  return val + 5;
});
=> {start: 10, end: 17}


pairs _.pairs(object)

各(key, value)のセットを1配列とした配列に置き換える。

_.pairs({one: 1, two: 2, three: 3});
=> [["one", 1], ["two", 2], ["three", 3]]


invert _.invert(object)

keyとvalueを逆にしたオブジェクトを返す。valueが一意である必要がある。
もし一意でなくてもエラーにはならないけど、あえて使うこともない。

_.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
=> {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};


create _.create(prototype, props)

第一引数をprorotypeとして、第二引数を値に持った新オブジェクトを作る。

function Stooge(name, age) {
  this.name = name;
  this.age = age;
};
Stooge.prototype.silly = true;
Stooge.prototype.say = function(){console.log('I am ' + this.name)};
var stoo = new Stooge('aaa', 'bbb');
var moe = _.create(stoo, {name: "Moe", silly: false});
=> {name: "Moe", silly: false}

moe.say();
=> I am Moe


functions _.functions(object)

メソッド名一覧を返す。

function Stooge(name, age) {
  this.name = name;
  this.age = age;
};
Stooge.prototype.silly = true;
var stoo = new Stooge('aaa', 'bbb');
_.functions(stoo);
=> ["say"]


findKey _.findKey(object, predicate, [context])

ArraysのfindIndexのオブジェクト(ハッシュ配列)版。predicateに該当するkeyを返す。

var moe = {name: "Moe", silly: false};
_.findKey(moe, function(value, key, obj) { return value == "Moe"; });
=> "name"
_.findKey(moe, function(value, key, obj) { return (key == "silly") && (value == "Moe"); });
=> undefined


extend _.extend(destination, *sources)

destinationを元に(継承して)、*sourcesで上書きしたオブジェクトを返す。

_.extend({name: 'moe'}, {age: 50});
=> {name: 'moe', age: 50}

_.extend({name: 'moe', age: 50}, {age: 40}, {silly: true});
=> {name: 'moe', age: 40, silly: true}


extendOwn _.extendOwn(destination, *sources)

extendとほぼ同じ。違いは*sourcesのprorotypeまで見に行くかどうか。
extendはprototypeまで見に行く、extendは見に行かない(ownまで)。

function Stooge(name) {
  this.name = name;
}
Stooge.prototype.silly = true;
_.extend({name: 'moe'}, new Stooge('hoge'));
=> {name: 'hoge', silly: true}
_.extendOwn({name: 'moe'}, new Stooge('hoge'));
=> {name: 'hoge'}


pick _.pick(object, *keys)

指定したkeyだけを持つオブジェクトを返す。keyにfunctionで条件指定も可能。

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
=> {name: 'moe', age: 50}
_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
=> {age: 50}


omit _.omit(object, *keys)

pickの逆版。条件に合致しないキーだけを持つオブジェクトを返す。

_.omit({name: 'moe', age: 50, userid: 'moe1'}, 'userid');
=> {name: 'moe', age: 50}
_.omit({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
=> {name: 'moe', userid: 'moe1'}


defaults _.defaults(object, *defaults)

extendの先勝ちみたいな。defaultsにあるkeyがobject上でundefinedの場合に、defaults値を設定する。
object上にあれば(≠undefined)、objectの値がそのまま保持される。

var iceCream = {flavor: "chocolate"};
_.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});
=> {flavor: "chocolate", sprinkles: "lots"}


clone _.clone(object)

シャローコピー。

var obj = {age: 20};
_.clone({name: 'moe', obj: obj});
=> {name: 'moe', obj:{age: 20}};
obj.age = 30;
=> {name: 'moe', obj:{age: 30}};


tap _.tap(object, interceptor)

chainで処理を繋げている時に、途中の状態を表示するという使い方をする。
(何でchainingじゃなくてObjects扱いなんだろう?)

_.chain([1,2,3,200])
  .filter(function(num) { return num % 2 == 0; })
  .tap(alert)
  .map(function(num) { return num * num })
  .value();
=> // [2, 200] (alerted)
=> [4, 40000]


has _.has(object, key)

keyがあるかどうかチェック。 hasOwnProperty()はobjectがnullだとエラーになるが、こちらはfalseを返してくれる。

_.has({a: 1, b: 2, c: 3}, "b");
=> true
_.has(null, "b");
=> false


property _.property(key)

obj[ key ]を返す関数を作る。使いドコロが、、、chainingで使う?

var stooge = {name: 'moe'};
'moe' === _.property('name')(stooge);
=> true


propertyOf _.propertyOf(object)

object[ key ]を返す関数を作る。objectを先に渡すところがpropertyとの違い。

var stooge = {name: 'moe'};
_.propertyOf(stooge)('name');
=> 'moe'


matcher _.matcher(attrs)

条件に合致する処理を行う関数を返す。
key == key && value == valueな判定の関数を作ってくれるので、filterの条件をさくっと作れる。

var ready = _.matcher({selected: true, visible: true});
var readyToGoList = _.filter(list, ready);


isEqual _.isEqual(object, other)

objectの各プロパティが同じならtrueを返す。
object == other がfalseでも中の値が完全同一ならtrueになる。

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true


isMatch _.isMatch(object, properties)

containsのハッシュ配列版みたいな。objectにpropertiesが含まれていればtrue。

var stooge = {name: 'moe', age: 32};
_.isMatch(stooge, {age: 32});
=> true


以下は判定系で名前から想像つくので簡単に。

isEmpty _.isEmpty(object)

空判定。値がないオブジェクト({})や、配列や文字列などのlengthを持つものでlength=0ならtrue。

isElement _.isElement(object)

DOMエレメント判定。

isArray _.isArray(object)

配列判定。

isObject _.isObject(value)

オブジェクト判定。

isArguments _.isArguments(object)

引数オブジェクト判定。

isFunction _.isFunction(object)

関数判定。

isString _.isString(object)

文字列判定。

isNumber _.isNumber(object)

数値判定。

isFinite _.isFinite(object)

無限大判定。

isBoolean _.isBoolean(object)

真偽値判定。

isDate _.isDate(object)

日時オブジェクト判定。

isRegExp _.isRegExp(object)

正規表現判定。

isError _.isError(object)

エラー判定。

isNaN _.isNaN(object)

非数判定。純粋にNaNかどうか判断する。undefinedはfalseになる。

isNull _.isNull(object)

null判定。undefinedはfalseになる。

isUndefined _.isUndefined(value)

undefined判定。nullはfalseになる。


Objectは数は多いが感覚的に理解できるものが多いので分かりやすい。

Underscore.jsおさらい4(Functions)

bind _.bind(function, object, *arguments)

関数のオブジェクト束縛。関数を変数にして引き回せるJavaScriptならでは。
Javaな自分には最初理解できなんだ。
関数内のthisをオブジェクトに割り当てる。
今は標準でbindが実装されているが、実装が曖昧という記述も見たのでしばらくは使うかも。

var func = function(greeting){ return greeting + ': ' + this.name };
func = _.bind(func, {name: 'moe'}, 'hi');
func();
=> 'hi: moe'


bindAll _.bindAll(object, *methodNames)

methodNamesで指定したメソッドだけbindする。未指定なら全メソッドが対象。

var buttonView = {
  label  : 'underscore',
  onClick: function(){ return 'click:' + this.label; },
  onHover: function(){ return 'hovering:' + this.label; }
};
_.bindAll(buttonView, 'onHover');
var click = buttonView.onClick;
var hover = buttonView.onHover;
click();    => click:undefined
hover();    => hovering:underscore


partial _.partial(function, *arguments)

bindはthisをbindするが、partialは引数をbindする感じ。
第一引数はbindせずに、第二引数をbindしたい、という場合は「_」を使えばいい。

var subtract = function(a, b) { return b - a; };
sub5 = _.partial(subtract, 5);
sub5(20);
=> 15

// Using a placeholder
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
=> 15


memoize _.memoize(function, [hashFunction])

関数をキャッシュして処理を早くする用途で使われる。

var fibonacci = _.memoize(function(n) {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
});


delay _.delay(function, wait, *arguments)

setTimeoutに近い感じで使える。waitミリ秒後にfunctionを実行する。

var log = _.bind(console.log, console);
_.delay(log, 1000, 'logged later');
=> 'logged later' // 1秒後にコンソール出力


defer _.defer(function, *arguments)

コールスタックが空になるまで処理を待つ。setTimeoutで0ミリ秒指定と似た感じ。

function() {
  alert('1');
  _.defer(function(){ alert('deferred'); });
  alert('2');
}
=> 1, 2, deferrdの順にalert表示


throttle _.throttle(function, wait, [options])

関数の呼び出しを制限できる。
waitミリ秒以内に関数が再度呼び出されても実行されないようになる。
optionsに{leading: false}を指定すれば、初回実行も指定秒待ってから実行される。
{trailing: false}を指定すれば、指定時間内に後続の呼び出しがあれば、待たずに実行がキャンセルされる。
特にleading: falseの時とか、特に引数とかbindした関数使うときは注意。

var f = _.throttle(function(n) {console.log(n + ':' + _.now());}, 100);
f(1);  => 現在ミリ秒表示
f(2);  => 1回目から100ミリ秒以上経っていれば実行、経っていなければ100ミリ秒後まで待って実行

var f = _.throttle(function() {console.log('-----:' + _.now());}, 100, {leading: false});
f();  => 100ミリ秒後に実行されるようにtimerセット
f();  => 開始から100ミリ秒以内であれば、1回目の呼び出しを破棄して開始から100ミリ秒後に実行されるようにtimerセット
// 後勝ちになる

var f = _.throttle(function() {console.log('-----:' + _.now());}, 100, {trailing: false});
f();  => 現在ミリ秒表示
f();  => 1回目から100ミリ秒以内であれば無視される、100ミリ秒以上なら実行


debounce _.debounce(function, wait, [immediate])

関数の呼び出しを制限できる。
throttleの{leading: false}と似たような動きになる。
immediateをtrueにすると、まず関数が実行されるのでthrottleの{trailing: false}と似た感じ。

once _.once(function)

一度しか実行できない関数にする。2回目以降の戻り値は1回目と同じ値になる。

var f = _.once(function() {console.log('-----:' + _.now());});
f();  => 実行
f();  => 実行されない


after _.after(count, function)

count回呼び出し以降しか実行されない。

var f = _.after(3, function() {console.log('-----:' + _.now());});
f();  => 実行されない
f();  => 実行されない
f();  => 実行
f();  => 実行


before _.before(count, function)

最初のcount - 1回までしか実行されない。
onceは_.before(2, function)として実装されている。

var f = _.after(3, function() {console.log('-----:' + _.now());});
f();  => 実行
f();  => 実行
f();  => 実行されない
f();  => 実行されない


wrap _.wrap(function, wrapper)

関数を別関数でラップして、元関数の前後に処理を入れられる。
AOPとかfilterとかそんなイメージ。

var hello = function(name) { return "hello: " + name; };
hello = _.wrap(hello, function(func) {
  return "before, " + func("moe") + ", after";
});
hello();
=> 'before, hello: moe, after'


negate _.negate(predicate)

predecateの戻り値(真偽値)を逆にする。

var f = _.negate(function(n){ return n % 2 == 0; });
_.find([-2, -1, 0, 1, 2], f);
=> -1


compose _.compose(*functions)

合成関数を作成する。
数学的にf(), g(), h()の合成関数はf(g(h()))となるらしいので、後ろの関数から順に処理される感じになる。

var greet    = function(name){ return "hi: " + name; };
var exclaim  = function(statement){ return statement.toUpperCase() + "!"; };
var welcome = _.compose(greet, exclaim);
welcome('moe');
=> 'hi: MOE!'

Function終了。タイマー系の理解がちょっと大変。

Underscore.jsおさらい3(Arrays)

JavaScript Underscore.js
first _.first(array, [n])

先頭の要素を返す。shiftと違い非破壊的。
nを指定すると先頭からn個分の要素を配列で返す。

initial _.initial(array, [n])

最後の要素を除いた配列を返す。
nを指定すると後ろからn個分除いた残りを返す。

_.initial([5, 4, 3, 2, 1]);
=> [5, 4, 3, 2]


last _.last(array, [n])

firstの逆。最後の要素を返す。

rest _.rest(array, [index])

initialの逆。最初の要素を除いた配列を返す。
多分first~restは、非破壊的なshift、popのニーズから作ったものだろう。

compact _.compact(array)

配列から「!hoge」== trueな要素を除いた配列を返す。

_.compact([0, 1, false, 2, '', 3]);
=> [1, 2, 3]


flatten _.flatten(array, [shallow])

多次元配列の全要素を1次元配列にする。
shallowにtrueを指定すると全て平らにせず、次元を1つ減らすだけで止める。

_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];

_.flatten([1, [2], [3, [[4]]]], true);
=> [1, 2, 3, [[4]]];


without _.without(array, *values)

valuesに指定した以外の値だけを含んだ配列を返す。
valuesにArrayオブジェクト指定はできないので、そんな時はdifferenceで。

_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
=> [2, 3, 4]


union _.union(*arrays)

重複要素は除きつつ配列を結合する。

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]


intersection _.intersection(*arrays)

全ての配列に含まれる要素だけを取り出す。

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]


difference _.difference(array, *others)

withoutに似ているが、除外条件を配列で指定できる。

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]


uniq _.uniq(array, [isSorted], [iteratee])

配列をユニーク化する。
ソート済みの配列であればisSortedにtrueを指定すれば処理が早くなる。
要素の一致条件は「===」なので、オブジェクトの配列の場合はデフォルトではユニーク化されない。
iterateeに一致条件の値を指定すればユニーク化できる。プロパティの文字列指定も化。

_.uniq([1, 2, 1, 4, 1, 3]);
=> [1, 2, 4, 3]

var array = [{id:1}, {id:2}, {id:3}, {id:1}, {id:2}];
_.uniq(array, function(element){ return element.id; });
=>[{id:1}, {id:2}, {id:3}]
_.uniq(array, 'id');
=>[{id:1}, {id:2}, {id:3}]


zip _.zip(*arrays)

配列をマージする。もしlengthが異なっていれば、undefinedで穴埋めされる。
多次元配列に値を持っている場合は、_.zip.applyで幸せになれる、とわざわざドキュメントに書いているのに、unzipで同じことができる謎。
unzipは一度削除された復活されたから、説明がそのまま残っているのだと思われる。
zipのソースを見ると、unzipに処理を丸投げしている。

_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
_.zip(['moe', 'larry', 'curly'], [30, 40], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", undefined, false]]


unzip _.unzip(*arrays)

unとついていてもzipの逆ではなく、多次元配列をzipする。
引数が*arraysになっているが、多分表記が誤っている。第一引数の多次元配列しか処理されない。

_.unzip([['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]])
=> ["moe", 30, true], ["larry", 40, false], ["curly", 50, false]


object _.object(list, [values])

object化、というかハッシュ配列化してくれる。多次元配列の場合はvaluesは不要。
listの各要素がkeyになるので、valuesの要素数の方が多ければvaluesの多い分は無視される。
listの方が多ければ、valueにundefinedが入る。

_.object(['moe', 'larry', 'curly'], [30, 40, 50]);
=> {moe: 30, larry: 40, curly: 50}

_.object([['moe', 30], ['larry', 40], ['curly', 50]]);
=> {moe: 30, larry: 40, curly: 50}


indexOf _.indexOf(array, value, [isSorted])

該当要素の番号を返すおなじみのやつ。だが基本使わないはず。Array.indexOfがECMAScript 5で実装されている。
(IE8以前を使っているような古代人は相手にしない)
ソート済みの場合はisSortedにtrue指定で処理が早くなるので、使うとすればそこぐらいか。

lastIndexOf _.lastIndexOf(array, value, [fromIndex])

indexOfの最後から版。isSortedオプションも無いので、使うことはないだろう。

sortedIndex _.sortedIndex(list, value, [iteratee], [context])

indexOfのもしもボックス版。
ソート済みのlistに対して、もしvalueが入るなら何番目かを返してくる。
オブジェクトの配列ならiterateeで条件を指定する。プロパティの文字列指定も化。

_.sortedIndex([10, 20, 30, 40, 50], 35);
=> 3

var stooges = [{name: 'moe', age: 40}, {name: 'curly', age: 60}];
_.sortedIndex(stooges, {name: 'larry', age: 50}, 'age');
=> 1


findIndex _.findIndex(array, predicate, [context])

indexOfが固定値の検索だが、findIndexは条件を関数で指定できる。
predicateにkey : valueのオブジェクトを指定して、条件を含むオブジェクトの検索も可。
findWhereのindexOf版みたいな感じになる。

_.findIndex([4, 6, 7, 12], function(num){ return num % 2 == 1; });
=> 2

var users = [{'id': 1, 'name': 'Bob', 'last': 'Brown'},
             {'id': 2, 'name': 'Ted', 'last': 'White'},
             {'id': 3, 'name': 'Frank', 'last': 'James'},
             {'id': 4, 'name': 'Ted', 'last': 'Jones'}];
_.findIndex(users, {name: 'Ted'});
=> 1
_.findIndex(users, {name: 'Ted', last: 'Jones'});
=> 3


findLastIndex _.findLastIndex(array, predicate, [context])

findIndexを後ろから。

range _.range([start], stop, [step])

指定した範囲までの連番が入った配列を作ってくれる。

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []

Array終了。

Underscore.jsおさらい2(Collections)

JavaScript Underscore.js

サンプルソースは本家ドキュメントから拝借。分かり辛いものだけ加筆・修正。

each _.each(list, iteratee, [context])

いわゆるforEach。
配列の場合はiterateeの引数に(element, index, list)が、オブジェクトの場合は(value, key, list)が使える。

_.each([1, 2, 3], function(element){ alert(element); });
=> alert(1)、alert(2)、alert(3)
_.each({one: 1, two: 2, three: 3}, function(element, key){ alert(element); });
=> alert(1)、alert(2)、alert(3)


map _.map(list, iteratee, [context])

iteratee処理結果の新しい配列ができる。

_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
_.map([[1, 2], [3, 4]], _.first);
=> [1, 3]


reduce _.reduce(list, iteratee, [memo], [context])

最後のiterateeの結果が返る。
処理中に値を保持できる変数(memo)が使えるので、最終的な合計値や計算結果を出す、という処理なんかで使う。 [memo]はmemoの初期値。

var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
=> 6
var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 10);
=> 16


reduceRight _.reduceRight(list, iteratee, memo, [context])

reduceを逆順で行う。

var list = [[0, 1], [2, 3], [4, 5]];
var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
=> [4, 5, 2, 3, 0, 1]


find _.find(list, predicate, [context])

条件(predicate)に合致する最初の値を返す。合致する値がなければundefined。

var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> 2


filter _.filter(list, predicate, [context])

条件(predicate)に合致する全ての要素を配列で返す。合致する値がなければ空配列。

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]


where _.where(list, properties)

条件のオブジェクト(properties)を含む要素を配列で返す。
filterと似ているが条件指定をオブジェクトに変えた感じ。
SQLのwhere(and)条件をオブジェクト指定するイメージ。

var listOfPlays = [
    {title: "Cymbeline", author: "Shakespeare", year: 1611},
    {title: "Othello", author: "Shakespeare", year: 1604},
    {title: "Lupin The 3rd", author: "Monkey Punch", year: 1967},
    {title: "The Tempest", author: "Shakespeare", year: 1611}
];
_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
    {title: "The Tempest", author: "Shakespeare", year: 1611}]


findWhere _.findWhere(list, properties)

find + where。whereの条件に合致する最初の要素を返す。

reject _.reject(list, predicate, [context])

filterの逆バージョン。predicateの条件に合致しない要素を配列で返す。
演算子を逆にしてfilter使う方が分かりやすくね?

var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [1, 3, 5]


every _.every(list, [predicate], [context])

条件(predicate)に全て合致すればtrue、1つでも異なればfalseを返す。

_.every([true, 1, null, 'yes'], function(element){ return element != null; });
=> false


some _.some(list, [predicate], [context])

everyのOR条件版。条件(predicate)に1つでも合致すればtrue、全て異なればfalseを返す。
everも同様だが、predicateを省略すると_.identityが指定されるので「!hoge」の判定となる。

contains _.contains(list, value, [fromIndex])

listにvalueが含まれていればtrueを返す。
formIndexを指定して検索開始位置を指定できる(けど、オブジェクトの場合は保証されないよな多分)。

_.contains([1, 2, 3], 3);
=> true


invoke _.invoke(list, methodName, *arguments)

listの各要素に対してmethodNameで指定したメソッド(関数)を実行する。

_.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
=> [[1, 5, 7], [1, 2, 3]]


pluck _.pluck(list, propertyName)

オブジェクトの配列から、指定したキー(propertyName)の値を取り出した配列を返す。
mapを簡素化した感じ。

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.pluck(stooges, 'name');
=> ["moe", "larry", "curly"]


max _.max(list, [iteratee], [context])

最大値を持つ要素を返す。listがオブジェクトの配列であればiterateeで判定対象の値を返す。

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.max(stooges, function(stooge){ return stooge.age; });
=> {name: 'curly', age: 60};


min _.min(list, [iteratee], [context])

最小値を持つ要素を返す。使い方はmaxと同じ。

sortBy _.sortBy(list, iteratee, [context])

iterateeで指定した条件でソートする。
iterateeに関数ではなく文字列を指定すると、該当プロパティがソートキーとなる。

_.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
=> [5, 4, 6, 3, 1, 2]

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.sortBy(stooges, 'name');
=> [{name: 'curly', age: 60}, {name: 'larry', age: 50}, {name: 'moe', age: 40}];


groupBy _.groupBy(list, iteratee, [context])

iterateeで指定した条件をキーにしたオブジェクト(ハッシュ配列)を返す。
iterateeに関数ではなく文字列を指定すると、該当プロパティの値がキーとなる。

_.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
=> {1: [1.3], 2: [2.1, 2.4]}

_.groupBy(['one', 'two', 'three'], 'length');
=> {3: ["one", "two"], 5: ["three"]}


indexBy _.indexBy(list, iteratee, [context])

groupByと似ているが、groupByがキーに対して該当要素を配列で保持するのに対して、indexByは1つしか持たない。
なので一意の項目をキー指定する前提。
もし一意でないキーを指定した場合は、使用される要素は後勝ちになる。

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.indexBy(stooges, 'age');
=> {
  "40": {name: 'moe', age: 40},
  "50": {name: 'larry', age: 50},
  "60": {name: 'curly', age: 60}
}

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 40}, {name: 'curly', age: 60}];
_.indexBy(stooges, 'age');
=> {
  "40": {name: 'larry', age: 50},
  "60": {name: 'curly', age: 60}
}


countBy _.countBy(list, iteratee, [context])

groupByと似ているが、要素自体ではなく要素数を保持したオブジェクトを返す。

_.countBy([1, 2, 3, 4, 5], function(num) {
  return num % 2 == 0 ? 'even': 'odd';
});
=> {odd: 3, even: 2}

var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 40}, {name: 'curly', age: 60}];
_.countBy(stooges, 'age');
=> {
  "40": 2,
  "60": 1
}


shuffle _.shuffle(list)

Fisher-Yates shuffleアルゴリズムでシャッフルされた配列を返す。非破壊的。

_.shuffle([1, 2, 3, 4, 5, 6]);
=> [4, 1, 6, 3, 5, 2] (当然実行する毎に結果は変わる)


sample _.sample(list, [n])

listからランダムに要素を取り出す。引数nを指定すれば、その数だけ配列で取り出す。

_.sample([1, 2, 3, 4, 5, 6]);
=> 4 (当然実行する毎に 以下略)

_.sample([1, 2, 3, 4, 5, 6], 3);
=> [1, 6, 2] (当然 以下略)


toArray _.toArray(list)

配列から配列を作る。
意味が分からないのでカンペしたところ、argumentsの処理に使うのが良いらしい。
あとハッシュ配列を配列化してくれる。順番は保証されないと思う。

(function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
=> [2, 3, 4]

_.toArray({a:1, b:2, c:3, d:4});
=> [1, 2, 3, 4]


size _.size(list)

素数を返す。配列ではlength使えばいいけど、ハッシュ配列でlengthの代わりに使う感じ。

_.size({one: 1, two: 2, three: 3});
=> 3


partition _.partition(array, predicate)

predicateの条件に合う要素と合わない要素に分けた配列を作る。

_.partition([0, 1, 2, 3, 4, 5], function(num){ return num % 2 == 1; });
=> [[1, 3, 5], [0, 2, 4]]

_.partition({a:1, b:2, c:3, d:4}, function(num){ return num % 2 == 1; });
=> [[1, 3], [2, 4]]


collection終わった・・・今日中に全部終わるかな。。。

Underscore.jsおさらい1(概要)

JavaScript Underscore.js

Underscoreのドキュメントを見直していたら、「これ使えてたわ」ということがあったので、機能一覧ぐらいは頭に叩き込むためにおさらいする。

参考
Underscore.js
遅すぎたUnderscore.js入門 - 全体像 - Qiita

全体概要

Underscoreはいわゆる便利系関数のJavaScriptライブラリ。
jQueryは不要で単体で使えるのが手軽。Backbone使うなら必須。

機能概要

  • Collections
    • forEachやソートといった、配列などの集合データの操作系
  • Arrays
    • 配列操作系、Collectionsは『配列+ハッシュ配列(オブジェクト)』が対象なのに対して、Arraysは純粋な配列のみ
  • Functions
    • JavaScriptではよくある関数を引数に取る関数群
  • Objects
    • keyやvalueの一覧取得、clone、extendといったオブジェクト操作系
  • Utility
    • ユーティリティ機能
  • Chaining
    • 関数の戻り値を別の関数の引数に・・・とすると階層が深くなって困るので、メソッドチェーンで書けるようにできる
    • Javaな人はJava8のコレクションでstream APIを想像すれば分かりやすいかも

context引数

引数に[context]がある場合は、contextとして渡したオブジェクトが関数内でthisとして扱える。
ざっくりループ系の機能についている感じ。

var obj = {a: 0};
_.each([1, 2, 3], function(element){this.a += element }, obj);
=> obj.a = 6

Backbone.Validationが空文字を許してくれない

backbone

Backboneでvalidateを簡単にしてくれるプラグインBackbone.Validation。
GitHub - thedersen/backbone.validation: A validation plugin for Backbone.js that validates both your model as well as form input

正規表現で入力値をチェックできるpatternを使ったら、空文字もエラー扱いになって困った。
ドキュメントのAdding custom validatorsを参考に、独自patternを作って対処。

_.extend(Backbone.Validation.validators, {
  customPattern: function(value, attr, customValue, model) {
    if(value !== ''){
      return Backbone.Validation.validators.pattern(value, attr, customValue, model);
    }
  }
}

空文字でなければ元からあるpatternバリデーション(Backbone.Validation.validators.pattern)を呼び出すだけ。
これでビルトインのnumberやemailパターンも使えるし、独自正規表現パターンも使える。
拡張方法が明記されているのはありがたい。

まーしゃる

つれづれ

先のエントリーにあるメソッドmarshalの意味を調べる。

marshal:(名詞) 元帥

なんだそりゃ、と思ったが動詞だと「(人・軍隊を)整列させる、整頓する」という意味らしい。
だから元帥なのね。
戦いの意味合いを持っているなら、マーシャルアーツのマーシャルはこの単語?

違った・・・martial arts
martial:(形容詞) 勇ましい、好戦的な

ただ発音は同じらしい。

それにしてもなんでarts(芸術)なんだろう、と思ったらWikipediaに書いてあった。
「武芸」が英語になったからか。
マーシャルアーツ - Wikipedia

マーシャルアーツと言えば「テリー・ボガード」が連想される古い人間のワタシ。