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

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

新規登録して質問してみよう
ただいま回答率
85.47%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

String

Stringは、ゼロ以上の文字から連続してできた文字の集合を扱うデータ型です。基本的にテキストを表すために使われます。

Q&A

解決済

3回答

481閲覧

C#のList分解について

wave_vague

総合スコア21

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

String

Stringは、ゼロ以上の文字から連続してできた文字の集合を扱うデータ型です。基本的にテキストを表すために使われます。

0グッド

0クリップ

投稿2024/04/16 08:07

実現したいこと

Listの中身
List<string> list = new List<string>(new string[] { "A1000", "A1001", "A1002" , "A1003", "A1004" , "A1005", "A1006" , "A1007", "A1008" , "A1009", "A1010" });
・Listが全て連番の場合
A1000~A1010

Listの中身
List<string> list = new List<string>(new string[] { "A1000", "A1001", "A1002" , "A1004" , "A1006" , "A1007", "A1008" , "A1009", "A1010" });
・Listが全て連番でない場合
A1000~A1002, A1004, A1006~A1010

のように表示したいです。

発生している問題・分からないこと

全て連番の場合はnotMatchListが0かどうかをチェックし、
list.First()とlist.Last()で実現できましたが、連番でない場合はどのように実現するかが分からないためご教示お願いいたします。

該当のソースコード

・連番の場合 List<string> list = new List<string>(new string[] { "A1000", "A1001", "A1002" , "A1003", "A1004" , "A1005", "A1006" , "A1007", "A1008" , "A1009", "A1010" }); ・連番でない場合 List<string> list = new List<string>(new string[] { "A1000", "A1001", "A1002" , "A1004" , "A1006" , "A1007", "A1008" , "A1009", "A1010" }); private void CheckSerial(List<string> list) { var notMatchList = new List<string>(); string number = ""; for (int i = 0; i <= list.Count(); i++) { if (i == list.Count() - 1 ) { break; } int first = int.Parse(list[i].Substring(1, 4)); int second = int.Parse(list[i + 1].Substring(1, 4)); if (first + 1 == second) { } else { notMatchList.Add(list[i + 1]); } } //連番 if (notMatchList.Count() == 0) { string first = list.First(); string last = list.Last(); number = first + "~" + last; } }

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

IndexOf()でnotMatchListのインデックスを取得し、それを活用しようとしましたが上手い方法が見つかりませんでした。

補足

特になし

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

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

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

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

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

guest

回答3

0

Linq を利用する場合です。

.NET Fiddle

Csharp

1using System; 2using System.Collections.Generic; 3using System.Linq; 4 5public class Program 6{ 7 public static void Main() 8 { 9 List<string> list = new List<string> { "A1000", "A1001", "A1002" , "A1004" , "A1006" , "A1007", "A1008" , "A1009", "A1010" }; 10 list.Select((e, i) => (e, i)) 11 .GroupBy(t => Int32.Parse(t.e[1..]) - t.i, (k, tg) => tg.Select(t => t.e)) 12 .Select((g, i) => (g, i)).ToList().ForEach(grp => { 13 if (grp.i > 0) { 14 Console.Write(", "); 15 } 16 Console.Write(grp.g.First()); 17 if (grp.g.Count() > 1) { 18 Console.Write($"〜{grp.g.Last()}"); 19 } 20 }); 21 Console.WriteLine(); 22 } 23}

投稿2024/04/16 10:27

melian

総合スコア19825

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

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

TN8001

2024/04/16 10:37

おーすごーい!! インデックスを引いてグループ化するのか頭いいなぁ
wave_vague

2024/04/17 00:22

ご回答ありがとうございます。 Linqの使用は考えていませんでした。 (今までLinqを使用したことがないため、まずはLinqについて勉強しようと思います) 大変勉強になりました。
guest

0

元コードをある程度尊重して書くとこんなんですかね?(あんま自信なし^^;

指定がないので.NET8です^^
ソートされているという前提・数字位置は固定

cs

1using System.Globalization; 2 3 4List<string> list = ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A1010"]; 5Console.WriteLine(CheckSerial(list)); // A1000~A1010 6 7list = ["A1000", "A1001", "A1002", "A1004", "A1006", "A1007", "A1008", "A1009", "A1010"]; 8Console.WriteLine(CheckSerial(list)); // A1000~A1002,A1004,A1006~A1010 9 10list = ["A1000", "A1001"]; 11Console.WriteLine(CheckSerial(list)); // A1000~A1001 12 13list = ["A1000"]; 14Console.WriteLine(CheckSerial(list)); // A1000 15 16list = []; 17Console.WriteLine(CheckSerial(list)); // 18 19//list = null!; 20//Console.WriteLine(CheckSerial(list)); // ArgumentNullException 21 22 23string CheckSerial(List<string> list) 24{ 25 ArgumentNullException.ThrowIfNull(list); 26 if (list.Count == 0) return ""; 27 if (list.Count == 1) return list[0]; 28 29 var result = list[0]; 30 31 for (var i = 0; i < list.Count - 1; i++) 32 { 33 var first = int.Parse(list[i][1..5], CultureInfo.InvariantCulture); 34 var second = int.Parse(list[i + 1][1..5], CultureInfo.InvariantCulture); 35 36 if (first + 1 == second) 37 { 38 if (result[^1] != '~') result += "~"; 39 if (i + 1 == list.Count - 1) result += list[i + 1]; 40 } 41 else 42 { 43 if (result[^1] == '~') result += list[i]; 44 result += $",{list[i + 1]}"; 45 } 46 } 47 48 return result; 49}

「LINQとかでスマートに書けないのかな?」と思いますがどうなんでしょうね。
c# - Convert list to number range string - Stack Overflow

逆の場合はスマートな方法がありました。
Does C# have built-in support for parsing page-number strings? - Stack Overflow
c# - Convert range of number in string to list of integer - Stack Overflow


文字列からだと汎用性がないのでintで受けるパターン(逆変換付き)
melianさんとBrian Payneさんのをまるパクリw

cs

1using System.Diagnostics; 2using System.Globalization; 3 4 5for (var i = 0; i < 10; i++) 6{ 7 var numbers = Enumerable.Range(0, 30) 8 .OrderBy(x => Guid.NewGuid()) 9 .Take(20) 10 .OrderBy(x => x) 11 .Take(Random.Shared.Next(20)) 12 .ToList(); 13 14 var rangeString = ToRangeString(numbers); 15 Console.WriteLine(rangeString); 16 17 var numbers2 = ParseRangeString(rangeString).ToList(); 18 Console.WriteLine(string.Join(",", numbers2)); 19 Console.WriteLine(); 20 21 Debug.Assert(numbers.SequenceEqual(numbers2)); 22} 23 24 25string ToRangeString(IEnumerable<int> source) 26{ 27 var r = source.Select((e, i) => (e, i)) 28 .GroupBy(t => t.e - t.i, (k, tg) => tg.Select(t => t.e)) 29 .Select((g, i) => $",{g.First()}{(1 < g.Count() ? $"~{g.Last()}" : "")}"); 30 31 return string.Join("", r).TrimStart(','); 32} 33 34IEnumerable<int> ParseRangeString(string source) 35{ 36 if (string.IsNullOrWhiteSpace(source)) return []; 37 38 return source.Split(',') 39 .Select(x => x.Split('~')) 40 .Select(p => new 41 { 42 First = int.Parse(p.First(), CultureInfo.InvariantCulture), 43 Last = int.Parse(p.Last(), CultureInfo.InvariantCulture) 44 }) 45 .SelectMany(x => Enumerable.Range(x.First, x.Last - x.First + 1)); 46}
3,5,8~10 3,5,8,9,10 1,3,5~8,10~14,16~19,21 1,3,5,6,7,8,10,11,12,13,14,16,17,18,19,21 2 2 2~3,6~8,10~14 2,3,6,7,8,10,11,12,13,14 1~2,4~5,7~10 1,2,4,5,7,8,9,10 0~1,4~8,10,12,14,17 0,1,4,5,6,7,8,10,12,14,17 2~6 2,3,4,5,6 0,2~8,10,15,18,20~23 0,2,3,4,5,6,7,8,10,15,18,20,21,22,23 1~3,5~7,9~13,16~17,19,21~22,24,26 1,2,3,5,6,7,9,10,11,12,13,16,17,19,21,22,24,26

投稿2024/04/16 10:25

編集2024/04/16 15:46
TN8001

総合スコア9326

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

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

wave_vague

2024/04/17 00:39

ご回答ありがとうございます。 .NETのバージョンを記載するのを失念しておりました。 (今回は.NET4.8で作成したコードです。) 連番になっている場合は、resultの後ろから1番目の要素が'~'になっているかをチェックし、'~'でない場合は連番表記にするために'~'を付与、 連番になっていない場合は、resultの後ろから1番目の要素が'~'になっているかをチェックし、連番でない場の表示である','を付与 要素を使用しる方法をいくつか試してみました(変換を駆使し、.Splitや.Remove等をうまい感じで使用できないか)が上手くいかなかったため、大変勉強になります。
guest

0

ベストアンサー

のように表示したいです。

との話ですが,とりあえず(その場で表示していくのではなくて)一旦グループ分けしてみました.

C#

1class Program 2{ 3 //グループ分け 4 static IEnumerable<string[]> Grouping( IReadOnlyList<string> list ) 5 { 6 if( !list.Any() )yield break; 7 8 var Group = new List<string>(); 9 int PrevItemValue = int.Parse( list[0].Substring(1,4) ); 10 Group.Add( list[0] ); 11 12 for( int i=1; i<list.Count(); ++i ) 13 { 14 int ItemValue = int.Parse( list[i].Substring(1,4) ); 15 16 if( PrevItemValue+1 == ItemValue ) 17 { 18 Group.Add( list[i] ); 19 } 20 else 21 { 22 yield return Group.ToArray(); 23 Group.Clear(); 24 Group.Add( list[i] ); 25 } 26 27 PrevItemValue = ItemValue; 28 } 29 yield return Group.ToArray(); 30 } 31 32 //Grouping()の結果を表示してみるテスト 33 static void Test( IReadOnlyList<string> list ) 34 { 35 foreach( var Group in Grouping(list) ) 36 { 37 if( Group.Length == 1 ) 38 { Console.WriteLine( Group[0] ); } 39 else 40 { Console.WriteLine( Group[0] + " - " + Group.Last() ); } 41 } 42 } 43 44 //Main 45 static void Main(string[] args) 46 { 47 Console.WriteLine( "1st data:" ); 48 Test( new string[] { "A1000", "A1001", "A1002" , "A1003", "A1004" , "A1005", "A1006" , "A1007", "A1008" , "A1009", "A1010" } ); 49 50 Console.WriteLine( "-----------------------" ); 51 Console.WriteLine( "2nd data:" ); 52 Test( new string[] { "A1000", "A1001", "A1002" , "A1004" , "A1006" , "A1007", "A1008" , "A1009", "A1010" } ); 53 54 Console.Read(); 55 } 56}

↑の出力結果はこんな感じ:

1st data: A1000 - A1010 ----------------------- 2nd data: A1000 - A1002 A1004 A1006 - A1010

[追記]
質問文のコードをなるべく使うようにするなら,例えばこんな感じでどうでしょう?

C#

1private void CheckSerial( List<string> list ) 2{ 3 var GroupLastIndexes = new List<int>(); //←名称変更,要素をintに変更.ここに連番グループの末尾要素のindexを収集することを考える. 4 string number = ""; 5 6 for (int i = 0; i <= list.Count(); i++) 7 { 8 if (i == list.Count() - 1) 9 { 10 break; 11 } 12 13 int first = int.Parse(list[i].Substring(1, 4)); 14 int second = int.Parse(list[i + 1].Substring(1, 4)); 15 16 if (first + 1 == second) 17 { 18 19 } 20 else 21 { 22 GroupLastIndexes.Add( i ); //← i をAddするように変更 23 } 24 } 25 26 if (GroupLastIndexes.Count() == 0) 27 { 28 string first = list.First(); 29 string last = list.Last(); 30 31 number = first + "~" + last; 32 } 33 else //elseの処理を追加 34 { 35 GroupLastIndexes.Add( list.Count()-1 ); 36 37 int iGroupStart = 0; 38 foreach( int iGroupLast in GroupLastIndexes ) 39 { 40 if( number.Any() )number += " , "; 41 42 if( iGroupStart == iGroupLast ) 43 { number += list[iGroupStart]; } 44 else 45 { number += list[iGroupStart] + "~" + list[iGroupLast]; } 46 47 iGroupStart = iGroupLast+1; 48 } 49 } 50 51 //※確認用に number の表示を追加 52 Console.WriteLine( number ); 53}

投稿2024/04/16 09:21

編集2024/04/16 10:15
fana

総合スコア11660

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

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

wave_vague

2024/04/17 00:14

ご回答ありがとうございます。 要素でなく要素番号を使用してグループ分けを行い、グループの最初の要素番号と最後の要素番号が一致していれば連番でない、一致していなければ連番になるという処理は実装中思いつきませんでした。 (要素番号を使用しようとしましたが、上手い処理が思いつきませんでした) 私が書いたコードを元に、実装していることもあり分かりやすく大変勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問