任意の回数繰り返してから次に行く配列
次のような配列をRubyのワンクエリで作ろうとして頭を悩ませた。
# それぞれの数字をn回繰り返してから、1ずつ加算し、mまでの配列
[1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5]
先に結論
以下を考えました。(もっと良い解答があるようです)
[*1..n].map{[*1..m]}.transpose.flatten
# 例
irb> [*1..5].map{[*1..10]}.transpose.flatten
=> [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10]
解説
# '*'をつけて配列展開
> [*1..5]
=> [1, 2, 3, 4, 5]
# .mapで1..5のそれぞれに対して、その中で[1..10]を配列展開
## mapを使うのは、mapは完成した配列を戻り値としてくれるため
> [*1..5].map{[*1..10]}
=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
# transposeで横の配列を縦にする
> [*1..5].map{[*1..10]}.transpose
=> [[1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3], [4, 4, 4, 4, 4], [5, 5, 5, 5, 5], [6, 6, 6, 6, 6], [7, 7, 7, 7, 7], [8, 8, 8, 8, 8], [9, 9, 9, 9, 9], [10, 10, 10, 10, 10]]
# flattenで平坦化した配列を返す
> [*1..5].map{[*1..10]}.transpose.flatten
=> [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10]
(追加)1飛ばしなど
# Numeric.step(limit, step=1)を用いる
> [*1..5].map{1.step(10,2).to_a}.transpose.flatten
=> [1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 9, 9, 9, 9, 9]
## 1.step(10,2)で、1から10まで2step(1飛ばし)になる
# または、rangeに対して、step()して、to_aで配列にする
> [*1..5].map{(1..10).step(2).to_a}.transpose.flatten
=> [1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 9, 9, 9, 9, 9]
もっと簡単に書けました。
https://qiita.com/ja-mik/items/e15858224398f8c27843#comment-427a684b60b4d9f1711a
配列掛け算してflat_mapすればよかった。
こちらの方がスマート!
素直に書くと
素直に書くとワンクエリじゃ無理な気がします。
a = []
[*1..n].each{|i| m.time{ a << i} }
p a
# 例
irb> a =[]
=> []
irb> [*1..10].each{|i|5.times{a<<i}}
=> [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
irb> a
=> [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10]
補足
繰り返しが2回ならzipを使って以下のようにできますが、n回ずつ繰り返したいとなるとこれではできませんね・・・。
> [*1..10].zip([*1..10]).flatten
=> [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]
まとめ
複数項目について、全パターンを作成する時に、n回繰り返してmまでカウントアップする配列を作りたいとなりました。
Rubyはそれがワンクエリでできるのが素晴らしい。