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

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

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

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

Q&A

解決済

3回答

813閲覧

Listの全要素の積集合を求めたい

Yumineko

総合スコア22

C#

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

2グッド

1クリップ

投稿2019/03/05 14:07

編集2019/03/05 14:08

前提・実現したいこと

C#で、Listの全要素の積集合を求めたいのですが、やり方が分かりません。
LinqにIntersectというメソッドがありますが、これは2つのListを比較するものです。
1つのListに含まれる全要素を対象にしたいのですが、foreachなどを使っても上手く求められません。

該当のソースコード

C#

1[Flags] 2public enum UnitElement { 3 Blue = (1 << 0), 4 Red = (1 << 1), 5 Male = (1 << 2), 6 Female = (1 << 3), 7 Human = (1 << 4), 8 Robot = (1 << 5), 9 Naked = (1 << 6), // 裸眼 10 Glass = (1 << 7), // 眼鏡 11 AllZero = 0b00000000, 12 AllOne = 0b11111111, 13} 14 15public clas Unit{ 16 public Element element; 17} 18 19public class Union { 20 public List<Unit> list = new List<Unit> (); 21 public Union () { } 22 public Union (List<Unit> unitList) { list = unitList; } 23 24 /// <summary> 25 /// 全てのUnit属性の論理積を返す。 26 /// </summary> 27 /// <returns>共通属性がない・1つしかUnitがない場合はAllZeroを返す</returns> 28 public UnitElement AndElement () { 29 UnitElement flag = UnitElement.AllZero; 30 if (IsEmpty () || list.Count == 1) return flag; 31 foreach (var unit in list) { 32 flag = flag == UnitElement.AllZero ? 33 unit.Element : unit.Element & flag; 34 } 35 return flag; 36 }

こんな感じです。Union.AndElement()を実行することで、Union.listにいる全Unitの共通属性を取得したいのです。
foreachで1つずつ抽出する場合、記憶するための変数(flag)へどう代入するかが悩みです。
Enum型はnullを許容しないので、初期値用のUnitElement.Allzeroを代入させてからforeachを回すのですが、
AllZeroは全てが0なので、単純に&で比較すると必ず0になってしまいます。
よって、AllZeroの場合は初期値とみなしてlistの要素をそのまま代入させてみましたが、
そうすると今までに比較した段階で0だった場合と区別ができません。

例)unit01とunit02に共通属性が1つもないままunit03と比較した場合

試したこと

論理積を求める記事を調べると、大抵は比較数分の変数を定義して&演算子で比較しています。

C#

1Unit unit01 = new Unit(); 2Unit unit02 = new Unit(); 3unit01.element = UnitElement.Blue | UnitElement | Male | UnitElement.Robot; 4unit02.element = UnitElement.Blue | UnitElement | Male | UnitElement.Human; 5UnitElement flag = unit01.element & unit02.element; // Blue, Male

このunit01や02が何人になった時でも対応できるようにlistの全要素で比較対象にしたいです。

DrqYuto👍を押しています

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

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

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

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

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

guest

回答3

0

数が1つ以上あるのがわかっているんだから、flagをAllOneかlist内の値で初期化してやれば後々Andをとっても結果は一緒です。

C#

1public UnitElement AndElement () { 2 if (IsEmpty () || list.Count == 1) return UnitElement.AllZero; 3 4 UnitElement flag = UnitElement.AllOne; //または list[0].Element 5 foreach (var unit in list) { 6 flag = unit.Element & flag; 7 } 8 return flag; 9}

投稿2019/03/05 14:24

toki_td

総合スコア2850

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

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

Yumineko

2019/03/05 14:30

なるほど……全部フラグを立てた初期値か、foreachの最初の値で初期化しちゃえばいいんですね。目からうろこでした。ありがとうございます。
guest

0

ベストアンサー

積集合ではなく各要素の論理積ですね。
Aggregate でできます。

C#

1Unit unit01 = new Unit(); 2Unit unit02 = new Unit(); 3unit01.element = UnitElement.Blue | UnitElement.Male | UnitElement.Robot; 4unit02.element = UnitElement.Blue | UnitElement.Male | UnitElement.Human; 5List<Unit> list = new[] { unit01, unit02 }.ToList(); 6 7UnitElement flag = list 8 .Select(a => a.element) 9 .Aggregate((a, b) => a & b);

投稿2019/03/05 19:39

編集2019/03/05 19:40
Zuishin

総合スコア28660

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

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

Yumineko

2019/03/05 21:02

おお、まさに求めていた機能です!Aggregateというメソッドがあるんですね。 ありがとうございます。
guest

0

public clas Unit{ public string[] elements; }

俺はビット演算に慣れていないので(ほぼやったことないので)、こういう形で、文字列集合処理でやるなぁ・・。
速度はビット演算のほうがあるけど、今時、PCは十分早いし・・・。

list.SelectMany(n=>n.elements) .GroupBy(n=>n) .Select(n=> new {n.Key,Count = n.Count()}) .Where(n=>n.Count == list.Count)

で、Listの中にすべてで存在するものはわかるはず。

投稿2019/03/05 14:42

kiichi54321

総合スコア1984

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問