初心者です。続き番号の配列の要素を以下のように〜で繋げて表示させたいと考えてます。
[1,2,3,6,7,9,25,26,27,30] //元の配列 [1~3,6~7,9,25~27,30] //これが欲しい。続き番号は~をつけてまとめて表示される。
やってみたこと
下のように、続き番号になる部分を抽出する方法で、型を文字列にするのは後回しにしましたが、どうにかやってみました。
結果
[[1,3],[6,7],9,[25,27],30]
ですが、私は知識に乏しく、より汎用性のある、特に関数を使った記述の仕方があると思います。
アドヴァイスなどいただけませんでしょうか。よろしくお願いいたします。
js
1let a = [1,2,3,6,7,9,25,26,27,30]; //元の配列 2let array = [];//新しく作る配列 3let count = 0; 4let start = 0, end = 0 5 6for (var n = 0; n < a.length; n++) { 7 switch(a[n] === a[n+1]-1) { 8 case true: 9 if(count === 0) { 10 start = n 11 } 12 count++ 13 continue; 14 break 15 case false: 16 if(count === 0) { 17 array.push(a[n]) 18 continue 19 } 20 end = n 21 count = 0 22 array.push([a[start],a[end]]) 23 break 24 } 25} 26 27console.log(array)
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答8件
0
こんにちは
再帰を使ってみました。
javascript
1const MAX_LENGTH = 100000; 2 3const f = ary => { 4 const len = ary.length; 5 6 if (len > MAX_LENGTH) { 7 throw new Error('too long !'); 8 } 9 10 if (len <= 1){ 11 return [ary]; 12 } 13 14 const head = [ary[0]]; 15 let tailIndex = -1; 16 17 for (let i=1; i < len; i++) { 18 if (ary[i-1] + 1 === ary[i]) { 19 head.push(ary[i]) 20 } else { 21 tailIndex = i; 22 break; 23 } 24 } 25 26 return tailIndex === -1 ? [ head ] : [ head ].concat(f(ary.slice(tailIndex))); 27}; 28
処理対象の配列の長さの上限 MAX_LENGTH
を(とりあえず) 100000 としています。
以下、上記の f()
を使った例です。
javascript
1const data = [1,2,3,6,7,9,25,26,27,30]; 2 3const result = f(data); 4 5console.log(result); 6 7console.log(result.map(e => e.length >=2 ? `${e[0]}〜${e[e.length-1]}` : `${e[0]}`)); 8
上記によって、以下のように出力されます。
[[1, 2, 3], [6, 7], [9], [25, 26, 27], [30]]
["1〜3", "6〜7", "9", "25〜27", "30"]
上記のコードを以下の jsFiddle に上げましたので、動作確認して頂ければと思います。
以上参考になれば幸いです。
追記1
上記の f()
を、 forループのかわりにfindIndex
を使って修正したものが以下です。
javascript
1const MAX_LENGTH = 100000; 2 3const f = ary => { 4 const len = ary.length; 5 6 if (len > MAX_LENGTH) { 7 throw new Error('too long !'); 8 } 9 10 if (len <= 1){ 11 return [ary]; 12 } 13 14 const i = ary.findIndex((e, i) => i>=1 && ary[i-1]+1 !== e); 15 16 return i < 0 ? [ ary ] : [ary.slice(0, i)].concat(f(ary.slice(i))); 17}; 18
- 上記の動作確認用のサンプル: https://jsfiddle.net/jun68ykt/78s9eabq/3/
追記2
上記で回答した関数 f(ary)
は、引数に空の配列 []
を渡されると [[]]
を返します。これが質問者さまにとって望ましいのかどうか不明ではありますが、このままにしておきます。
追記3
すでに解決済みとなっていますが、少し修正したものを挙げておきます。
先の追記1に挙げたコード を、以下の5点につき、修正しました。
- 関数名を
divide
とした。 if (len <= 1){ return [ary]; }
は不要だったので削除- 配列を連結するのに、
concat
のかわりにスプレッド構文を使用 - 処理対象の上限を判定するために、引数の配列の長さではなく再帰の深さでチェックするように修正
- 変数名
i
が、一行の中の異なる2カ所で使われていたので修正
上記の修正後のものが以下です。
javascript
1const MAX_RECURSIVE_DEPTH = 1000; 2 3const divide = (ary, depth=0) => { 4 5 if (depth > MAX_RECURSIVE_DEPTH) { 6 throw new Error('Recursive depth is over the limit.'); 7 } 8 9 const endOfSeries = ary.findIndex((e, i) => i>=1 && ary[i-1]+1 !== e); 10 11 return endOfSeries < 0 ? 12 [ary] : 13 [ary.slice(0, endOfSeries), 14 ...divide(ary.slice(endOfSeries), depth+1)]; 15};
- 動作確認用のサンプル: https://jsfiddle.net/jun68ykt/78s9eabq/9/
投稿2019/05/29 23:29
編集2019/06/07 12:38総合スコア9058
0
参考までに
javascript
1var a=[1,2,3,6,7,9,25,26,27,30]; 2var b=[]; 3a.forEach(function(x,y){ 4 if(y==0 || a[y-1]+1!==x){ 5 b.push([x,null]); 6 }else{ 7 b[b.length-1][1]=x; 8 } 9}); 10console.log(b);// [[1,3],[6,7],[9,null],[25,27],[30,null]] 11var c=b.map(function(x){return x[1]==null?x[0]:x.join("-")}); 12console.log(c); // ["1-3","6-7",9,"25-27",30]
投稿2019/05/30 00:46
総合スコア114583
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
以下、参考までに載せておきます。
全体の処理を関数化すると共に、結合する文字を選択できるようにしておきました。
なお、「serializeArrayに渡された配列に要素がないときは、すぐに空の配列を返して終了する」など、場合わけを細かくすればもう少し処理が軽くなると思います。ご自分で試してみてください。要素数2つまでは、単純比較の方が楽だろうと思います。
Array.prototype.reduce()
とArray.prototype.map()
はよく使うので、使い方を覚えておくといいと思います。他にも、配列(Array)にどんなメソッド(関数)が用意されているかを確認しておくと、今後かなり楽になると思います。MDNのこちらのページの「メソッド」の部分(左のサイドバー)を確認しておくことをおすすめします。
JavaScript
1function serializeArray (array, separator) { 2 return array 3 .reduce(function(chunkedArray, current) { 4 if (!chunkedArray.length) { 5 chunkedArray.push([ current ]) 6 7 return chunkedArray 8 } 9 10 const lastChunk = chunkedArray[chunkedArray.length - 1] 11 const lastElement = lastChunk[lastChunk.length - 1] 12 13 if (lastElement + 1 === current) { 14 lastChunk.push(current) 15 } else { 16 chunkedArray.push([ current ]) 17 } 18 19 return chunkedArray 20 }, []) 21 .map(function(chunk) { 22 const first = chunk[0] 23 24 if (chunk.length === 1) { 25 return first + '' 26 } 27 28 const last = chunk[chunk.length - 1] 29 30 return first + separator + last 31 }) 32} 33 34// テスト用の配列を定義 35const array1 = [] // 要素なし 36const array2 = [ 1 ] // 連番なし 37const array3 = [ 1, 2 ] // 連番のみ 38const array4 = [ 1, 2, 3, 6, 7, 9, 25, 26, 27, 30 ] // 連番・単独の混合 39 40// 各配列に関数を適用 41const chunkedArray1 = serializeArray(array1, '~') 42const chunkedArray2 = serializeArray(array2, '~') 43const chunkedArray3 = serializeArray(array3, '~') 44const chunkedArray4 = serializeArray(array4, '~') 45 46// 出力して確認 47console.log(chunkedArray1) // -> [] 48console.log(chunkedArray2) // -> [ '1' ] 49console.log(chunkedArray3) // -> [ '1~2' ] 50console.log(chunkedArray4) // -> [ '1~3', '6~7', '9', '25~27', '30' ]
投稿2019/05/29 18:22
編集2019/05/29 18:25総合スコア1260
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/30 04:21
2019/05/30 15:23
0
データの整理と文字化を分けたら分かりやすいと思いました。
動かしてないので間違いがあったら申し訳ない。
JavaScript
1function hoge(ar) { 2 if (ar.length == 0) return []; 3 if (ar.length == 1) return [ar]; 4 let ret = []; 5 let tmp = [ar[0]]; 6 for(let i = 1; i < ar.length; i++) { 7 if (ar[i - 1] + 1 != ar[i]) { 8 ret.push(tmp); 9 tmp = []; 10 } 11 tmp.push(ar[i]); 12 } 13 ret.push(tmp); 14 return ret; 15} 16 17function piyo(ar, mk) { 18 let ret = []; 19 let tmp = ''; 20 for(let i = 0; i < ar.length; i++) { 21 tmp = ar[i][0] + ''; 22 if (ar[i].length > 1) { 23 tmp = tmp + mk + ar[i].slice(-1)[0]; 24 } 25 ret.push(tmp); 26 } 27 return ret; 28} 29 30let a = [1,2,3,6,7,9,25,26,27,30]; //元の配列 31let array = piyo(hoge(a), '~'); 32console.log(array);
投稿2019/05/29 23:58
編集2019/05/30 14:42総合スコア7458
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/30 06:21
2019/05/30 14:44
0
提示されたコードより長くなってしまった。
javascript
1 2class A { 3 constructor () { 4 this.r = [ ]; 5 this.reset (); 6 } 7 8 add (n) { 9 if (this.a == null) 10 this.reset (n); 11 else if (n - this.b == 1) 12 this.b = n; 13 else 14 this.push ().reset (n); 15 return this; 16 } 17 18 push () { 19 let {a, b} = this; 20 this.r.push (a - b ? a +'~' + b: '' + a); 21 return this; 22 } 23 24 reset (n = null) { 25 this.a = this.b = n; 26 return this; 27 } 28 29 get result () { 30 this.push (); 31 return this.r; 32 } 33} 34 35let ary = [1,2,3,6,7,9,25,26,27]; 36let r = ary.reduce ((a, b) => a.add (b), new A); 37 38console.log (r.result); 39
アドヴァイス
投稿2019/05/29 18:23
退会済みユーザー
総合スコア0
0
面白そうな話題ですね。
いろいろな回答が上がっていて見ていて楽しいです。他の言語での実装なども見てみたいものです。
私の実装
Array.reduce()内でオブジェクトぐるぐる回して実現してみました。
js
1const input = [1, 2, 3, 6, 7, 9, 25, 26, 27, 30]; 2const result = input.slice(1).reduce((a, x) => { 3 let result = {prev: x}; 4 if (x !== a.prev + 1) { 5 result.output = a.output.concat(a.start === a.prev ? a.start : `${a.start}~${a.prev}`); 6 result.start = x; 7 } 8 return {...a, ...result}; 9 }, 10 { output: [], prev: input[0], start: input[0] } 11).output; 12console.log(result);
input !== []
な前提ですが、いかがでしょうか。
同処理をワンライナーに
できそうだったのでワンライナーにしてみました。
み...見にくい。
js
1const input = [1, 2, 3, 6, 7, 9, 25, 26, 27, 30]; 2const result = input 3 .slice(1) 4 .reduce((a, x) => ({ 5 ...a, 6 prev: x, 7 ...(x !== a.prev + 1 8 ? { 9 output: a.output.concat(a.start === a.prev ? a.start : `${a.start}~${a.prev}`), 10 start: x, 11 } 12 : {}), 13 }), 14 { output: [], prev: input[0], start: input[0] } 15).output; 16console.log(result);
yambejpさまの回答をワンライナーで
追加: yambejpさまの回答が簡潔で素晴らしかったので、ワンライナーで書き直してみました。
js
1const input = [1, 2, 3, 6, 7, 9, 25, 26, 27, 30]; 2const result = input 3 .reduce( 4 (a, x, i) => 5 i === 0 || x !== input[i - 1] + 1 6 ? [...a, [x, null]] 7 : [...a.slice(0, -1), [a[a.length - 1][0], x]], 8 [] 9 ) 10 .map(x => (x[1] === null ? x[0] : x.join('-'))); 11console.log(result); // ["1-3","6-7",9,"25-27",30]
いい感じ。
自己満足の領域ゆえ、回答としては不適切かもしれませんが、せっかくなので投稿しておきます。
投稿2019/05/31 17:47
総合スコア118
0
面白そうだったので自分もよく使っているRamda.jsで試してみました。
sample.js
1const { groupWith, last } = require('ramda'); 2 3const numbers = [1, 2, 3, 6, 7, 9, 25, 26, 27, 30]; 4 5const groups = groupWith((a, b) => a + 1 === b, numbers); // => [ [ 1, 2, 3 ], [ 6, 7 ], [ 9 ], [ 25, 26, 27 ], [ 30 ] ] 6 7const pressedGroup = groups.map(group => { 8 if (group.length === 1) { return `${group}`;} // => [ 9 ] => '9' 9 const initialNumber = group[0]; // => [ 1, 2, 3 ] => 1 10 const lastNumber = last(group); // => [ 1, 2, 3 ] => 3 11 const groupString = `${initialNumber}~${lastNumber}`; // => '1~3' 12 return groupString; 13}); 14 15console.log(pressedGroup); // => [ '1~3', '6~7', '9', '25~27', '30' ] 16
投稿2019/06/06 19:03
総合スコア8
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
5年ほど前にQiitaにアップしたコードです
https://qiita.com/Uchikoba/items/d05906db6308395cf11d
投稿2019/06/04 05:23
総合スコア16
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/30 06:12 編集
2019/05/30 06:58