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

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

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

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

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

3回答

3863閲覧

C# foreachの処理で配列に要素を追加したい

go-kawano

総合スコア15

C#

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

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2018/06/26 11:15

C#で以下のような処理を実装しようとしています。

処理内容
文字列の配列を持つリストに対して、ある文字列で検索をかけ、ヒットすれば、
ヒットした配列に対して要素を追加するというものです。

サンプルプログラムとしては、

C#

1 static void Main(string[] args) 2 { 3 int i; 4 int j; 5 6 string[] searchlist = new string[3] { "犬", "猫", "ゴリラ" }; 7 8 string[] item1 = new string[3] { "ワニ", "猫", "犬" }; 9 string[] item2 = new string[3] { "コアラ", "キリン", "猫" }; 10 11 List<string[]> list = new List<string[]>(); 12 13 list.Add(item1); 14 list.Add(item2); 15 16 foreach(var item in list) 17 { 18 for (i = 0; i < searchlist.Length; i++) 19 { 20 if (item.Contains(searchlist[i]) == true) 21 { 22 for (j = 0; j < searchlist.Length; j++) 23 { 24 if (item.Contains(searchlist[j]) == false) 25 { 26 //itemに要素を追加したい 27 string[] temp = item; 28 Array.Resize(ref temp, temp.Length + 1); 29 temp[temp.Length - 1] = searchlist[j]; 30 31 list.Remove(item); //foreachの中でlistを変わっているので例外が発生 32 list.Add(temp); 33 34 break; 35 } 36 } 37 } 38 } 39 } 40 } 41

listが持っている配列全てに対して検索を書けるために、foreachで処理をしていますが、
検索がヒットした際に、要素を追加する処理を入れると例外が発生しています。

例外の原因は理解しているのですが、他の方法が思いつかず、質問させて頂いている次第です。

ご教示の程よろしくお願い致します。

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

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

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

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

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

guest

回答3

0

「foreachでList<T>の追加削除をしたい」のであれば、resultlistを用意するということになりますが、やりたいことは何でしょうか?
C#はやりたいことが素直に実装できる言語なので、本当にやりたいことを日本語で書くとよりよいヒントがteratailでは得られると思います。

コードサンプルを私は「searchlistのいずれか一つでも含む配列に対して、searchlistの含まれていない要素を足し込む」だと理解しました。それがやりたいことならUnionを使って下記のように書くことができます(item2も同じように書けます)。

csharp

1 static void Main(string[] args) 2 { 3 string[] searchlist = new string[3] { "犬", "猫", "ゴリラ" }; 4 5 string[] item1 = new string[3] { "ワニ", "猫", "犬" }; 6 string[] item2 = new string[3] { "コアラ", "キリン", "猫" }; 7 var item1Merge = (item1.Intersect(searchlist).Any()) ? item1.Union(searchlist).ToArray() : item1; // searchlistを1つでも含んでいたら、searchlistのうち、持っていないものを足す 8 Console.WriteLine(string.Join(",", item1Merge)); 9 }

追記>>
今回の目的でIntersectはやりすぎなのかもしれない。

csharp

1var item2Merge = item2.Amy(x=>searchlist.Contains(x)) ? item2.Union(searchlist).ToArray() : item2;

<<追記

Linqを使えないというのでは、C#を使う意味がないのですが、どうしてもforeachでやらないと気がすまないとしたら

csharp

1 static void Main(string[] args) 2 { 3 string[] searchlist = new string[3] { "犬", "猫", "ゴリラ" }; 4 5 string[] item1 = new string[3] { "ワニ", "猫", "犬" }; 6 string[] item2 = new string[3] { "コアラ", "キリン", "猫" }; 7 8 List<string[]> list = new List<string[]> {item1, item2}; 9 var result = new List<string[]>(); 10 11 foreach(var item in list) 12 { 13 var hashset = new HashSet<string>(item); 14 for (var i = 0; i < searchlist.Length; i++) //foreach(var matchItem in searchlist)とすべき 15 { 16 if (item.Contains(searchlist[i])) //item.Contains(matchItem) 17 { 18 for (var j = 0; j < searchlist.Length; j++) //foreach(var addItem in searchlist)とすべき 19 { 20 hashset.Add(searchlist[j]); // HashSetは重複要素を追加してもなかったことにしてくれる(HashSet.Add()の返り値がfalseになるがエラーにならない) //hashset.Add(addItem) 21 } 22 } 23 } 24 result.Add(hashset.ToArray()); 25 } 26 Console.WriteLine(string.Join(",", result.First())); 27 Console.WriteLine(string.Join(",", result.Last()));

のようにHashSet<T>を使えば、arrayのresizeとか、searhlist[j]が含まれていないことの確認とかいう無駄な処理をとばせます。

ついでながら、boolに対して== trueとするのはやめたほうがいいです。item.Contains() == trueのところです。falseも!item.Contains()というのを早急に身に付けたほうがいいです。
それと、listをforeachしているのに、何の必然性もないのにsearchlistをforにしているのもやめたほうがいいです。

投稿2018/06/26 12:15

編集2018/06/27 07:26
papinianus

総合スコア12705

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

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

go-kawano

2018/06/27 00:43 編集

ご回答ありがとうございます。 サンプルプログラムの意図は仰る通りです。 var item1Merge = (item1.Intersect(searchlist).Any()) ? item1.Union(searchlist).ToArray() : item1; 上記コードも理解しました。linq構文の凄さを実感しました。勉強します。 foreachを使っている理由は、実際には、itemが1000以上存在し、listに格納されています。 listの各itemに対して、「searchlistのいずれか一つでも含む配列に対して、searchlistの含まれていない要素を足し込む」という処理を行うために、listをforeachで処理しました。 itemが多数ある場合も、linqで実現できるのであれば、是非教えて頂きたいです。 また、多数のご指摘もありがとうございます。
papinianus

2018/06/27 07:36 編集

linqに件数の上限はありませんし、たかだか1000件でしょ?searchlistが1000件だとしても2000件。List<T>をいくつも作ったり、何回も追加削除したり、Array.Resizeを1000回もやるほうが負荷が高いのではないでしょうか。 いちおう、Intersectは「searchlistを1つでも含んでいるか」というだけでやるには過剰な仕組みなので、追記>><<追記のところに別のやりかたとして、item2Mergeを追加しました。ただコンパイルや実際に実行したときにIntersectしてからAnyするのがこれに比べてはっきり遅くなるかというと微妙かなと思っています。 ただ、速さという観点でいうと、早過ぎる最適化という問題を調べてみてください。コードを動作するドキュメントと捉えたときに「item1とsearchlistの共通部分Intersectがあったときに、和集合Unionを求める、というほうが後々理解しやすいと思います。また速さを考えるのであればPLINQやasync/awaitといった非同期処理や並列処理を考えるべきでしょう。
go-kawano

2018/06/29 00:06

Linq構文の知識が足りず、foreachに頼ってしまう部分がありました... 大変勉強になりました!ご指摘ありがとうございました。
guest

0

ベストアンサー

HashSet使えば良いです。(ただし、順序がいれかわるのでそれを解決したければもうすこし手を加える必要がありますが)

csharp

1 2using System.Linq; 3using System.Collections.Generic; 4 5var item1Set = new HashSet<string>(item1); 6var item2Set = new HashSet<string>(item2); 7 8// 要素が増えてくるとHashSetに一旦入れたほうが処理が早くなります 9var allItemSet = new HashSet<string>(item1); 10foreach(var item in item2) { 11 allItemSet.Add(item); 12} 13 14foreach( var s in searchlist) { 15 if(!allItemSet.Contains(s)) continue; 16 17 item1Set.Add(s); // 重複要素は追加されないので、なにもチェックする必要なし 18 item2Set.Add(s); 19} 20return new List<string[]>(){ 21 item1Set.ToArray(), 22 item2Set.ToArray() 23}; 24 25// itemXが複数バージョン 26var itemLists = new List<string[]>(){item1, item2, ...} 27 .Select(items => new HashSet<string>(items)) 28 .ToList(); 29var allItemSet = new HashSet<string>(itemLists.SelectMany(_ => _)); 30 31foreach(var s in serachlist) { 32 if(!allItemSet.Contains(s)) continue; 33 34 foreach(var itemSet in itemLists) { 35 itemSet.Add(s); 36 } 37} 38return itemLists.Select(itemSet => itemSet.ToArray()).toList();

投稿2018/06/27 06:45

編集2018/06/27 08:00
takezoux2

総合スコア3

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

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

papinianus

2018/06/27 07:39

質問をどう捉えるかの解釈問題かもしれませんが、`if (item.Contains(searchlist[i]) == true)`を無視しているので、item1やitem2、searchlistの内容を変えたときに、得られる結果が異なることになります。
takezoux2

2018/06/27 07:55

失礼、その部分を失念していました。修正します。
go-kawano

2018/06/29 00:03

ご回答ありがとうございます!大変勉強になりました。
guest

0

itemはいじらずに、
結果格納用のlistを追加すればいけますな

投稿2018/06/26 11:23

y_waiwai

総合スコア87719

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問