質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

8回答

3253閲覧

JavaScript 配列操作のより良い記述について

orori

総合スコア42

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

2グッド

4クリップ

投稿2019/05/29 15:55

編集2019/05/29 16:01

初心者です。続き番号の配列の要素を以下のように〜で繋げて表示させたいと考えてます。

[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)
HAC, sota_u👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

追記2

上記で回答した関数 f(ary) は、引数に空の配列 [] を渡されると [[]] を返します。これが質問者さまにとって望ましいのかどうか不明ではありますが、このままにしておきます。

追記3

すでに解決済みとなっていますが、少し修正したものを挙げておきます。

先の追記1に挙げたコード を、以下の5点につき、修正しました。

  1. 関数名を divide とした。
  2. if (len <= 1){ return [ary]; } は不要だったので削除
  3. 配列を連結するのに、concat のかわりにスプレッド構文を使用
  4. 処理対象の上限を判定するために、引数の配列の長さではなく再帰の深さでチェックするように修正
  5. 変数名 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};

投稿2019/05/29 23:29

編集2019/06/07 12:38
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/05/30 06:12 編集

ありがとうございます! 他の方の回答も解読しておりましたが、もっともシンプルでわかりやすかったです。 実際に自分が限られた時間内で書く上で非常に参考になりました
jun68ykt

2019/05/30 06:58

どういたしまして。 > 実際に自分が限られた時間内で書く上で非常に参考になりました とのことでよかったです ????
guest

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

yambejp

総合スコア114583

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/05/30 06:32

シンプルに理解できました。要素にもれなくラベルを与え、二回めの処理をわかりやすくされていますね。 明解な記述ですが、学ぶべき点が多く、勉強になりました。 ありがとうございました!!
guest

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
NozomuIkuta

総合スコア1260

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/05/30 04:21

解読するのに時間がかかってしまいましたが、大変勉強になりました。 各関数については、教科書的には学んでおりましたが、全く自分で使えてはいませんでした。 関数以外にも、今回いただいたものから、複数の処理をメソッドチェーンでつなげるやり方、 配列の最初の処理のやり方、変数の名前の付け方など、多くを学びました。 これを雛形にして、学んでいきたいです。 ありがとうございました!!
NozomuIkuta

2019/05/30 15:23

参考になったようでよかったです。 なぜベストアンサーになったのかはわかりませんけど...笑 「処理」という観点で言えば、jun68yktさんの、再帰関数(recursive function)を使った回答の方がより「ちゃんとしたアルゴリズムを組み立てられる」人のものだと思います。もしもっと興味があれば、「関数型プログラミング」や「functional programming javascript」などのキーワードで調べてみると面白いと思います。reduceやmapにも出くわすはずです。
guest

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
takasima20

総合スコア7458

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/05/30 06:21

ありがとうございます!ご指摘の通り二つに分けたほうがいいですね。 hoge関数の方は、うまく動いていますが、piyoの方が、最初の配列を飛ばしてしまっています。 時間がある時に、今一度確認してみます。 ありがとうございました!
takasima20

2019/05/30 14:44

あー、piyoのループは i=0 でしたね。 修正しました。 //コピペがバレバレです。(汗
guest

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/05/30 05:53 編集

解読するのに時間がかかってしまいましたが、こんな書き方もできるんですね! JSでclassからインスタンスを作成して処理するやり方は初めてです、非常に勉強になりました。 自分の中に取り入れて、使えるようにしていきたいです。 あとアドバイスの表記、そういうことなのですね!了解です。
退会済みユーザー

退会済みユーザー

2019/05/30 06:17

普通に答えたら面白くないので、オブジェクト指向っぽく。 状態の変化を細分化して組み立てる感じ。
guest

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

HAC

総合スコア118

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orori

2019/06/03 08:22 編集

回答が遅くなりたいへん失礼いたしました。 ワンライナーとは、メソッドチェーンで繋げて一行にしてしまうということですね。 いただいた回答も非常に理解しやすいと思います。 恥ずかしながら、私にはどの回答がもっともよいのかを判断する力はありませんが、 このページを折に触れて見返し学んでいこうとおもっています。
guest

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

bobchin

総合スコア8

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

5年ほど前にQiitaにアップしたコードです
https://qiita.com/Uchikoba/items/d05906db6308395cf11d

投稿2019/06/04 05:23

Uchikoba

総合スコア16

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問