いまどこ?
###配列に関する章です
1 . 遅すぎたUnderscore.js入門 - 全体像
2 . 遅すぎたUnderscore.js詳述 - Collections編
3 . 遅すぎたUnderscore.js詳述 - Arrays編 ←いまここ
4 . Functions
5 . Objects
6 . Utility
7 . Chaining
##この章の全体的な感想
最初は「配列かー、JS標準機能で十分と思ったけど、どうしてやるんだろ?」てなくらいに思っていたのですが、見て行くうちにその背景には_.initialでargumentsオブジェクトを配列として捉えることとか、色々深そうな話があると思ったのですが、掘り下げはそこそこにしてAPIを淡々とみていきます。
あとまぁ、ぶっちゃけこれはやりすぎじゃね?っていう逆に煩雑だなって思うところもありましたが、やりすぎるのがUnderscore.js。
##注意点
本家のコードを加筆・書き換えしたり、メタファー(たとえ)としてあえて厳密でない言葉で書いたりしていますのでご了承願います。
また、重複する説明が削られていくので、上から読んでいくことを推奨します。
間違いやアドバイスなどありましたらコメントかhp0isme@gmail.comまでお願いします。
##first
####_.first(array, [n])
配列の最初の要素を返します。
先日のCollections編より大分シンプルな印象が。
var result = _.first([5, 4, 3, 2, 1]);
console.log(result); // 5
第2引数に数字を入れると、その数分の要素を切り抜いて配列で返します。
これは使うポイントがあるか微妙です。
var result = _.first([5, 4, 3, 2, 1],4);
console.log(result); // [5,4,3,2]
##initial
####_.initial(array, [n])
配列の最後の要素を削除した配列を返します。
var result = _.initial([5, 4, 3, 2, 1]);
console.log(result); // [5,4,3,2]
initialなんのため?
このinitialというのはJavaScriptの関数のargumentsが配列ではなくオブジェクトだからという理由で作られた経緯があるっぽいです。
くわしくはstackoverflow(http://stackoverflow.com/questions/27192043/why-underscore-says-initial-is-especially-useful-on-the-arguments-object)
さらに、第2引数に数字を入れると、その数字分の要素を取り除いて配列で返します。
ここはこまかーーいことかも知れませんが、_.firstと比較すると体感できるでしょう。
//_.initialに第2引数「2」を入れると
//最後の2つが削られる
var result_i = _.initial([5, 4, 3, 2, 1],2);
console.log(result_i); // [5,4,3]
//_.firstに第2引数「2」を入れると
//最初の2つを切り取って返される
var result_f = _.first([5, 4, 3, 2, 1],2);
console.log(result_f); // [5,4]
##last
####_.last(array, [n])
その名の通り、配列の最後の値を取り出して返します。
var result = _.last([5, 4, 3, 2, 1]);
console.log(result); // 1
第2引数にnを入れると、最後のn個を取り出して配列で返します。
var result = _.last([5, 4, 3, 2, 1] , 3);
console.log(result); // [3,2,1]
##rest
####_.rest(array, [index])
第2引数に入れた値以降の残り(rest = のこり)を配列で返します。
第2引数がなければ、1を入れたときと同じ結果に。
まぁ、コードを見て感じて見ましょう。
var result = _.rest([5, 4, 3, 2, 1]);
console.log(result); // [4,3,2,1]
result = _.rest([5, 4, 3, 2, 1],2);
console.log(result); // [3,2,1]
##compact
####_.compact(array)
JavaScriptでは「false」として扱われるあたいを配列から除去します。
具体的には「false, null, 0, "", undefined , NaN」がそれにあたるそうです。
ここらへん、今あまり哲学的に考えても仕方ないかもしれません。
変な挙動を起こしたくない時などに。
var result = _.compact([0, 1, false, 2, '', NaN, undefined, 3]);
console.log(result); // [1,2,3]
##flatten
####_.flatten(array, [shallow])
入れ子になってる値なども全てひっくるめて1階層の配列にします。(flatten = 平らにする)
var result = _.flatten([1, [2], [3, [[4]]]]);
console.log(result); // [1,2,3,4]
##without
####_.without(array, *values)
第2引数以降の値が該当すれば除去して(without = 〜なしで)配列で返します。
第2,3,4〜と後方はいくらでも追加可能です。
var result = _.without([1, 2, 1, 0, 3, 1, 4], 0,1);
console.log(result); // [2,3,4]
result = _.without([1, 2, 1, 0, 3, 1, 4], 0,1,2,4);
console.log(result); // [3]
##union
####_.union(*arrays)
引数に入れた配列をまとめて1つの配列にします。その時、被っている値はユニークにします。
配列の引数はいくつでもOK。
var result = _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
console.log(result); // [1,2,3,101,10]
##intersection
####_.intersection(*arrays)
複数の配列すべてに共通してある部分を抜き出して配列にします。(intersection = 交差)
unionや、ここら辺はデータベース系のクエリにインスパイアされた系ですかね。
var result = _.intersection([1, 2, 3], [101, 2, 1, 10, 3], [3, 2, 1, 5]);
console.log(result); // [1,2,3]
##difference
####_.difference(array, *others)
intersectionの逆です。つまり、共通していないものを抜き出します。
その時の軸が1で、第2以降の引数は条件になります。これも言葉で言うのもあれですのでコードで。
var result = _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
console.log(result); // [1,3,4]
result = _.difference([1, 2, 3, 4, 5], [5, 2, 10],[2, 4, 3]);
console.log(result); // [1]
##uniq
####_.uniq(array, [isSorted], [iteratee])
基本は配列の値をユニークなものに整理してくれることです。
var result = _.uniq([1, 2, 1, 3, 1, 4]);
console.log(result); // [1,2,3,4]
第2引数「isSorted」がtrueならば更に早いアルゴリズムを採用するらしいです。(信じよう)
その時は数字の順番にしっかりソートされていることが条件。
var result = _.uniq([1, 1, 2, 2, 3, 3, 3, 4],true);
console.log(result); // [1,2,3,4]
第2引数を高階関数にすると順番に処理ができます。
var result = _.uniq([1, 1, 2, 2, 3, 3, 3, 4],alert);
console.log(result); // 順番にアラートされる
##zip
####_.zip(*arrays)
引数に複数渡されてきた配列をまとめて、その配列同士が対応した、良い感じの配列にします(w)
良い感じとは?こんなかんじ。
[ドル、円、ユーロ]
[100、200,300]
なら
[[ドル,100],[円,200],[ユーロ,300]]
まぁ、見てみましょう。
var result = _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
console.log(result); // [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
また、これが配列が入れ子になったマトリックス(格子)、つまり配列の中に[[配列],[配列],[配列]]のような感じだった場合はjs標準機能のapplyを駆使してできるようです。
var arrayOfRowsOfData = [['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]];
result = _.zip.apply(_, arrayOfRowsOfData);
console.log(result);
##object
####_.object(list, [values])
配列 → オブジェクトに変換します。
パターンは2つ。
パターン1
[[ドル,円,ユーロ],[100,200,300]] → {ドル:100, 円:200, ユーロ:300}
var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
console.log(result); // {moe: 30, larry: 40, curly: 50}
パターン2
[[ドル,100],[円,200],[ユーロ,300]] → {ドル:100, 円:200, ユーロ:300}
result = _.object([['moe', 30], ['larry', 40], ['curly', 50]]);
console.log(result); // {moe: 30, larry: 40, curly: 50}
##indexOf
####_.indexOf(array, value, [isSorted])
配列の中のとある値が何番目にあるかを数字で返します。複数あれば最初にあった位置を返します。
var result = _.indexOf([1, 2, 3, 2], 2);
console.log(result); // 1
また、第3引数isSortedがtrueだとより早いアルゴリズムを採用。ただしソートされてることが条件です。また出ましたね。
var result = _.indexOf([1, 2, 3], 3, true);
console.log(result); // 2
##lastIndexOf
####_.lastIndexOf(array, value, [fromIndex])
IndexOfの「最初にあった位置」というのが「最後にある位置」に置き換わったバージョン。
var result = _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
console.log(result); // 4
もしなければ「-1」を返します。
var result = _.lastIndexOf([1, 2, 3, 1, 2, 3], 6);
console.log(result); // -1
第3引数があれば、その地点から探索を開始。(けっこう紛らわしいと思いましたが・・)
var result = _.lastIndexOf([1, 2, 3, 1, 2, 3], 1, 3);
console.log(result); // 3
##sortedIndex
####_.sortedIndex(list, value, [iteratee], [context])
第2引数valueが、もし配列にあったのなら何番目に入るかを探索して返します。
ランキング機能とか、価格帯検索などに使えそうです。
var result = _.sortedIndex([10, 20, 30, 40, 50], 35);
console.log(result); // 3
またこれは高階関数(iteratee部分)やcontext(前回出てきた例のthis)を差し込めますが、以前記述したので割愛します。
以下は公式サイトにあった少しトリッキーな使い方。
オブジェクトが羅列されている配列に対して、オブジェクトを用意してage(年齢)という基準だと何番目?みたいにやってます。
var stooges = [{name: 'moe', age: 40}, {name: 'curly', age: 60}];
_.sortedIndex(stooges, {name: 'larry', age: 50}, 'age');
// 1
##range
####_.range([start], stop, [step])
これはPythonインスパイア系の配列生成メソッドですね。
単一の引数だと0から作る。
_.range(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2つ引数があれば1-2まで。
_.range(1, 11);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
第3引数が配列の要素数パターン
_.range(0, 30, 5);
// [0, 5, 10, 15, 20, 25]
第2引数まで、値を第3引数分インクリメントしていくパターン
_.range(0, -10, -1);
// [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
ゼロ単一だと空配列
_.range(0);
//[]
##お話してみませんか?
Underscore.jsや、新技術、ゲーム制作などの話相手募集中です。
なんでもいいのでお話(Skype or 都内)してみませんか?
hp0isme@gmail.comまでお気軽にご連絡どうぞ。