Underscore.jsおさらい6(Utility、Chaining)
noConflict _.noConflict()
underscoreは「_」がデフォルトだが、他のライブラリで使っていると衝突するので衝突防止。
var underscore = _.noConflict();
identity _.identity(value)
引数自身を返す。f(x) = xの処理。underscoreのiterateeデフォルト判定。
var stooge = {name: 'moe'}; stooge === _.identity(stooge); => true
constant _.constant(value)
value自身を返す関数を作る。
var stooge = {name: 'moe'}; stooge === _.constant(stooge)(); => true
noop _.noop()
空の関数 function(){} を返す。
times _.times(n, iteratee, [context])
n回分 iterateeを実行する。各結果の戻り値が詰められた配列を返す。
iterateeの引数はindex。
_.times(5, function(n) { return n * n; }); => [0, 1, 4, 9, 16]
random _.random(min, max)
min以上、max以下の整数値乱数を返す。
mixin _.mixin(object)
underscore関数を新たに定義する。
_.mixin({ capitalize: function(string) { return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase(); } }); _("fabio").capitalize(); => "Fabio" _.capitalize("mario"); => Mario
iteratee _.iteratee(value, [context])
underscoreのiteratee用関数を作成する。
valueがnullなら.identity、オブジェクトなら.matcher、関数ならその関数、それ以外(文字列とか)なら_.propertyを返す。
uniqueId _.uniqueId([prefix])
1から順番にカウントアップした数字を返す。prefixがあれば接頭語として付ける。
_.uniqueId(); => 1 _.uniqueId('id'); => id2 _.uniqueId('no_'); => no_3
escape _.escape(string)
HTMLエスケープする。
unescape _.unescape(string)
HTMLアンエスケープする。
result _.result(object, property, [defaultValue])
オブジェクトのプロパティ値を返す。
指定したプロパティがメソッドならばメソッドの戻り値を返す。
propertyに指定したキー、メソッドが無い場合、defaultValueが指定されていればdefaultValueの値が返る。
_.result(object, 'cheese'); => "crumpets" _.result(object, 'stuff'); => "nonsense" _.result(object, 'meat', 'ham'); => "ham"
now _.now()
現在時刻のミリ秒を返す。
template _.template(templateString, [settings])
backboneを使うなら再優先で覚えるべきもの。
文字列テンプレートにオブジェクトの値をマッピングさせる、いわゆるテンプレートエンジン。
JavaならVelocity、PHPならSmartyをイメージすればいい。
<%= %>はそのまま、<%- %>はHTMLエスケープして出力する。<% %>を使えばコードも埋め込める。
_.templateSettingsでテンプレート文字を好きに変更できる。
var compiled = _.template("hello: <%= name %>"); compiled({name: 'moe'}); => "hello: moe" var template = _.template("<b><%- value %></b>"); template({value: '<script>'}); => "<b><script></b>" var compiled = _.template("<% print('Hello ' + epithet); %>"); compiled({epithet: "stooge"}); => "Hello stooge" _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g }; var template = _.template("Hello {{ name }}!"); template({name: "Mustache"}); => "Hello Mustache!"
chaning
underscoreの各関数は関数呼び出し風にも書ける。
_.map([1, 2, 3], function(n){ return n * 2; }); _([1, 2, 3]).map(function(n){ return n * 2; });
そのため、特にコレクション系なんかで複数の処理をまとめてやりたいときに、メソッドチェーンで書ける。
Javaな人はJava8のStream APIと言えば分かる。
chain _.chain(obj)
value _(obj).value()
chainはメソッドチェーン用の関数。引数のobjをラップする。
valueは最終的な値の取り出し用。
_.chain(lyrics) .map(function(line) { return line.words.split(' '); }) .flatten() .reduce(function(counts, word) { counts[word] = (counts[word] || 0) + 1; return counts; }, {}) .value(); => {lumberjack: 2, all: 4, night: 2 ... }
おさらい、完了!
Underscore.jsおさらい5(Objects)
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)
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)
※サンプルソースは本家ドキュメントから拝借。分かり辛いものだけ加筆・修正。
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(概要)
Underscoreのドキュメントを見直していたら、「これ使えてたわ」ということがあったので、機能一覧ぐらいは頭に叩き込むためにおさらいする。
参考
Underscore.js
遅すぎたUnderscore.js入門 - 全体像 - Qiita
全体概要
Underscoreはいわゆる便利系関数のJavaScriptライブラリ。
jQueryは不要で単体で使えるのが手軽。Backbone使うなら必須。
機能概要
- Collections
- forEachやソートといった、配列などの集合データの操作系
- Arrays
- 配列操作系、Collectionsは『配列+ハッシュ配列(オブジェクト)』が対象なのに対して、Arraysは純粋な配列のみ
- Functions
- JavaScriptではよくある関数を引数に取る関数群
- Objects
- keyやvalueの一覧取得、clone、extendといったオブジェクト操作系
- Utility
- ユーティリティ機能
- Chaining
context引数
引数に[context]がある場合は、contextとして渡したオブジェクトが関数内でthisとして扱える。
ざっくりループ系の機能についている感じ。
var obj = {a: 0}; _.each([1, 2, 3], function(element){this.a += element }, obj); => obj.a = 6
Backbone.Validationが空文字を許してくれない
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パターンも使えるし、独自正規表現パターンも使える。
拡張方法が明記されているのはありがたい。