配列の中に、
複数の番号を格納しているとき、
それをまとめて表示する「綺麗なロジック」を
教えていただけないでしょうか?
ruby
1 2data=["1","2","3","5","7","8","9","10","11","12","15","16"] 3
このようなデータがある時に
ruby
11~3,5,7~12,15,16
という
「人が見てきれいな」出力を作りたいのですが、
「綺麗なロジック」が思いつきません。
熟練の方、
短く、早く、美しいロジックを
ご教授願えないでしょうか?
私がrubyやVBAを使っているのでサンプルはrubyですが、
rubyやVBAが第一希望ですが、他の言語でもOKです。
ですがが、
「言語特有の(モジュール等を使った)ロジック」でない方がありがたいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
PHPの例が出ていないので載せておきます。ネストした配列の初期化が容易にできるPHPらしい解法です。
if文や三項演算子などによる条件分岐が1つも無いのが特徴です。
整数配列の連続区間をハイフンで連結してグループ化する定番のアレ - Qiita
php
1$data = [1, 2, 3, 4, 5, 8, 9, 10, 13, 14, 20, 22]; 2foreach ($data as $i => $v) { 3 $pairs[$v - $i][isset($pairs[$v - $i])] = $v; 4} 5foreach ($pairs as $pair) { 6 $ranges[] = implode('~', $pair); 7} 8echo implode(',', $ranges);
投稿2017/08/04 20:21
編集2017/08/04 20:45総合スコア5223
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/08/04 21:22
2017/08/05 05:27
退会済みユーザー
2017/08/05 05:56
2017/08/18 02:04
0
ベストアンサー
RubyであればEnumerable#chunk_whileが使えます。
上のリンク先のマニュアルにまさしくそのような出力をするための例が載っています。それを参考にまとめて書くと次のようになります。
Ruby
1data = ["1", "2", "3", "5", "7", "8", "9", "10", "11", "12", "15", "16"] 2puts data.map(&:to_i) 3 .chunk_while { |a, b| b == a.succ } 4 .map { |x| x.size < 3 ? x : "#{x.first}~#{x.last}"} 5 .join(',')
ですが、chunk_whileのような隣同士をみてグルーピングするという関数やメソッドが用意されている言語は見当たらないです(group_byのようなグルーピングであればHaskellやLINQでみらえますが)。ということで、chunk_whileでしていることをEnumerable#injectで置き換えます。
Ruby
1data = ["1", "2", "3", "5", "7", "8", "9", "10", "11", "12", "15", "16"] 2puts data.map(&:to_i) 3 .inject([]) { |a, x| a.empty? || a.last.last.succ != x ? 4 a + [[x]] : 5 a.take(a.size - 1) + [(a.last + [x])] } 6 .map { |x| x.size < 3 ? x : "#{x.first}~#{x.last}"} 7 .join(',')
投稿2017/08/01 22:30
総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2017/08/02 01:12
0
ruby で書いてみました。
行数はすこし多いですが、他の言語にも翻訳しやすいかと思います。
ruby
1# 連続した数値毎に分割する 2def seq_array(array) 3 ans = [] 4 loop { 5 break if array.empty? 6 # 数字が連続している先頭部分と残りに分割する 7 div = array.partition.with_index {|x, idx| x == idx + array[0] } 8 ans << div[0] 9 array = div[1] 10 } 11 ans 12end 13 14def seq_array_to_s(array) 15 array.map do |seq| 16 (seq.length > 2) ? "#{seq[0]}〜#{seq[-1]}" : seq.join(',') 17 end.join(',') 18end 19 20data = ["1", "2", "3", "5", "7", "8", "9", "10", "11", "12", "15", "16"] 21seq_array = seq_array(data.map(&:to_i).sort) 22puts seq_array_to_s(seq_array)
先頭の数字が連続している部分を取り出す処理をまず書きました。
それをループさせて残り部分が空になるまで繰り返します。(再帰で書き直すこともできるかもしれない)
すると、最初の数列全体を 連続した部分毎に分割することができます。
あとは、この分割数列を文字列化する処理を書きました。
分割処理と文字化処理が明確に分離しているので、文字列書式を変更するのは容易だと思います。
追記 2017-08-06
seq_array を loop でなく、group_by で書き直すことができました。
さらに数値範囲を示す文字をパラメータで指定できるようにしてみました。
ruby
1# 連続した数値毎に分割する 2def to_seq_array(array) 3 array.group_by.with_index {|x, idx| x - idx }.values 4end 5 6def to_range_array(seq_array, range_str = '〜') 7 seq_array.map { |seq| seq.size > 2 ? "#{seq[0]}#{range_str}#{seq[-1]}" : seq } 8end 9 10data = ["1", "2", "3", "5", "7", "8", "9", "10", "11", "12", "15", "16"] 11seq_array = to_seq_array(data.map(&:to_i)) 12 13range_array = to_range_array(seq_array) 14puts range_array.join(',') 15 16range_array = to_range_array(seq_array, "..") 17puts range_array.join(' ')
実行結果:
$ ruby xxx.rb 1〜3,5,7〜12,15,16 1..3 5 7..12 15 16
投稿2017/08/04 17:53
編集2017/08/06 13:05総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2017/08/06 05:21
0
初心者ですが挑戦してみました。
Ruby
1# encoding: utf-8 2 3data = ["1","2","3","5","7","8","9","10","11","12","15","16"] 4 5data.map!(&:to_i) 6 7d = (data.min..data.max).to_a 8k = d - data 9 10k.each do |_k| 11 d[d.index(_k)] = '/' 12end 13 14d = d.join(',').split('/').map do |e| 15 e = e.split(',').reject(&:empty?).map(&:to_i) 16 e.size > 2 ? "#{e.min}~#{e.max}" : e 17end 18 19puts d.reject(&:empty?).join(', ') 20# => 1~3, 5, 7~12, 15, 16 21
投稿2017/08/01 19:13
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
Pythonで書いてみました。
python
1data,lst,ans = ["1","2","3","5","7","8","9","10","11","12","15","16"],[],[] 2for i,n in enumerate(data): 3 if not lst or int(n)-int(lst[len(lst)-1]) == 1: 4 lst.append(n) 5 if i == len(data)-1:ans += lst 6 else: 7 if i == len(data)-1:lst.append(n) 8 if len(lst) <= 2:ans += lst 9 else:ans += ["%s~%s"%(lst[0],str(int(lst[0])+len(lst)-1))] 10 lst.clear() 11 lst.append(n) 12print(ans) 13 14#実行結果 15['1~3', '5', '7~12', '15', '16']
投稿2017/08/01 17:22
編集2017/08/01 17:24総合スコア1392
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
あまり綺麗ではないですが,Ruby で書いてみました.
ruby
1data = ["1","2","3","5","7","8","9","10","11","12","15","16"] 2 3d = data.map{|x| x.to_i}.sort.uniq 4ret = [] 5tmp = [d.shift] 6d.each do |x| 7 if tmp.last + 1 == x then 8 tmp << x 9 else 10 ret << tmp 11 tmp = [x] 12 end 13end 14ret << tmp 15 16puts ret.map{|x| 17 if x.length > 2 then 18 "#{x.first}~#{x.last}" 19 elsif x.length > 1 then 20 "#{x.first}, #{x.last}" 21 else 22 "#{x.first}" 23 end 24}.join(", ") 25# 1~3, 5, 7~12, 15, 16
投稿2017/08/01 16:48
総合スコア147
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
楽しそうだったので書いてみました。perlで
$ perl -e ' > @data=qw(1 3 5 7 8 9 10 11 12 15 16); > $i = 0; > ($start,$output) = ("",""); > foreach (sort {$a <=> $b} @data) { > if ($start eq "") { > $start=$_; > } > if ($_ + 1 != $data[$i+1]) { > if ( $start == $_ ) { > $output .= sprintf "%d,",$_; > } elsif ( $start + 1 == $_ ) { > $output .= sprintf "%d,%d,",$start,$_; > } else { > $output .= sprintf "%d-%d,",$start,$_; > } > $start = "" ; > } > $i++; > } > printf "%s\n", $output ; > ' 1,3,5,7-12,15,16,
投稿2017/08/01 16:10
総合スコア2840
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。