今日の作業中(というか勉強中)、配列を任意の数の要素に分割したいとなりました。ことばだとわかりづらいですね。たとえばこんな感じ。
[1,2,3,4,5,6,7,8,9].divide(4) => [[1,2,3,4],[5,6,7,8],[9]] [1,2,3,4,5,6,7,8,9].divide(3) => [[1,2,3],[4,5,6],[7,8,9]]
rubyでいえばeach_slice
ですね(るりま)。今回はこれをjavascriptで実装しました。といってもそれほど難しいことはなく、さらっとかけそうです。
Array.prototype.divide = function(n){ var ary = this; var idx = 0; var results = []; var length = ary.length; while (idx + n < length){ var result = ary.slice(idx,idx+n) results.push(result); idx = idx + n } var rest = ary.slice(idx,length+1) results.push(rest) return results; } ary = [1,2,3,4,5,6,7,8,9] dividedAry1 = ary.divide(4); console.log(dividedAry1); // => [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9 ] ] dividedAry2 = ary.divide(3); console.log(dividedAry2); // => [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]
できました。需要はあるかどうかわかりませんが。
ちなみに今回コーディング中に引っかかったのはslice
の使い方。[0,1,2,3,4].slice(0,3)
としたとき、てっきり[0,1,2,3]
が戻り値になると思っていたのです。が、これは間違い。実際は[0,1,2]
が戻り値です。これが最初気づかず、なかなか苦労させられました。
どうやらslice
はふたつ目の引数end
を指定したとき、end
の直前までを取り出すようなのです。事実MDNにも次のように書かれています。
slice は end 自体は含めず、その直前まで取り出します。
slice(1, 4) は 2 番目の要素から 4 番目の要素まで (添字が 1, 2, 3 の要素) を取り出します。
end は負の添字を使って、配列の終わりからのオフセットを表すことができます。
slice(2, -1) は配列の 3 番目の要素から、最後から 2 番目の要素まで取り出します。
end が省略された場合、slice は配列の最後まで取り出します。
これは知らなかった。slice
は比較的使用頻度の高いメソッドのはずですが…… 如何に「なんとなく」使っていたのかがよくわかりますね。
もうひとつ注意したいのはslice(0)
。これはレシーバのオブジェクトをコピーします。Rubyでいうところのdup
ですね(るりま)。
ary = [1,2,3]; fake = ary; // 参照渡し copy = ary.slice(0); ary[0] = 2; console.log(ary); //=> [2,2,3] console.log(fake); //=> [2,2,3] console.log(copy); //=> [1,2,3]
それにしてもなぜこんな書き方をするのか? 不思議に思ってMDNを読んでみたところ―――ふむふむ、slice
は第2引数を指定しなかった場合、オブジェクトの終わりまで取り出すのか。つまりslice(0)
はthis[0]
からその終わりまで取り出していることになり、結果としてあたかもレシーバがコピーされたかのように見えるわけですね。
またひとつ賢くなってしまった。こういう細かな仕様を調べていくのもプログラミングの醍醐味ですよね。