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

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

ただいまの
回答率

90.51%

  • C#

    8507questions

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

  • for

    272questions

    for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

  • LINQ

    126questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

二重のforループをLINQへ

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 235

locoJr.

score 2

■事前情報
クラスA
クラスB
クラスC
があるとします。

クラスAはList<クラスB>をプロパティで持ちます。
クラスBはList<クラスC>をプロパティで持ちます。
つまり、クラスAとクラスBは1:n、クラスBとクラスCも1:nという関係になり、ツリー構造で構成されています。

■本題
とあるクラスCの1要素が判明している状態で、それが含まれるクラスBを特定する内容のLINQを検討していました。
for文では思いつくのですが、LINQでなかなか取得できません。

for(int b=0;b<クラスB.Count;b++;){
    for(int c=0;c<クラスC.Count;c++;){
        if(クラスA.クラスB[b].クラスC[c] == 判明しているクラスC){
            クラスB B=クラスA.クラスB[b];
        };
    };
};

直接オブジェクトを取得するやり方と、indexを取得して参照するやり方とあるかと思いますが、ご助力をお願いできないでしょうか。


2019/03/15追記
クラスB群、クラスC群は全て異なる要素になっています。
つまり、特定のクラスC要素であれば、特定のクラスBが導出可能です。
上記情報が漏れておりました。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

こんなかな?

class Program
{
    public static void Main(string[] args)
    {
        var soruce = new ClassA
        {
            ClassBs = new List<ClassB>
            {
                new ClassB
                {
                    ClassCs = new List<ClassC>
                    {
                        new ClassC
                        {
                            Element = 0
                        },
                        new ClassC
                        {
                            Element = 1
                        }
                    }
                },
                new ClassB
                {
                    ClassCs = new List<ClassC>
                    {
                        new ClassC
                        {
                            Element = 2
                        },
                        new ClassC
                        {
                            Element = 3
                        }
                    }
                }
            }
        };

        var fixedC = new ClassC
        {
            Element = 2
        };

        var foundBs = soruce.ClassBs
            .Where(b => b.ClassCs
                .Where(c => c.Element == fixedC.Element).Any());
    }

}

class ClassA
{
    public List<ClassB> ClassBs { get; set; }
}

class ClassB
{
    public List<ClassC> ClassCs { get; set; }
}

class ClassC
{
    public int Element { get; set; }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/15 05:34

    ご回答ありがとうございます。

    foundBs = soruce.ClassBs
    .Where(b => b.ClassCs
    .Where(c => c.Element == fixedC.Element));
    までは考えていたのですが、boolにしないといけないところでひっかかりできていませんでした。
    Anyをつければ良かったのですね。

    追記したようにクラスBとクラスCは重複なく存在するため、以下のようになりました。

    クラスB foundB = source.ClassBs.Where(b => b.ClassCs.Where(c => c.Element == fixedC.Element).Any()).First();

    キャンセル

+1

SelectManyで2重のシーケンスを展開して(BとCのタプルにして)やって、それをフィルタにかけたらいいです。

foreach(var found in listB.SelectMany(b => b.listC, (classB, classC) => (classB, classC))
                        .Where(bc => bc.classC == knownClassC)
                        .Select(bc => bc.classB))
{

}


もし対象となるCが1つしかないとわかっているなら、First/FirstOrDefaultで。

var found = listB.SelectMany(b => b.listC, (classB, classC) => (classB, classC))
        .Where(bc => bc.classC == knownClassC)
        .Select(bc => bc.classB)
        .First();

※ C#7.1以上でコンパイルしてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/12 08:45

    考え方は正しいと思いますが、コードが間違っていませんか?

    キャンセル

  • 2019/03/12 22:04

    ホントだ、、、テストコードから回答用に書き換えたときに盛大にミスってました、、、
    ご指摘ありがとうございます。

    キャンセル

  • 2019/03/15 05:22 編集

    ご回答ありがとうございます。

    タプルが何なのかこれまで気にした事がありませんでしたが、ご回答いただいた内容を踏まえ確認し、意味や使い方をそれとなく把握することができました。

    今回はなぜかC#7.0でやるという、意味不明なプロジェクトのおかげ(せい)で、使用するこができませんでしたが、頭の引き出しに入れたいと思います。

    ----------
    追記
    バージョンを明記すべきでしたね。

    キャンセル

+1

個人的には、普通にネストしてしまったほうがわかりやすくていいんじゃないかな?って思います。

foreach(var res in a.classB.Where(b => b.classC.Contains(searchTarget)))
foreach(var res in a.classB.Where(b => b.classC.Any(c => c.ID == searchTarget.ID)))

// 最初の一つだけでいいならWhereをFirstかFirstOrDefaultにする
res = a.classB.FirstOrDefault(b => b.classC.Contains(searchTarget));


嫌がる人は嫌がると思いますけれども。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/15 05:30

    ご回答ありがとうございます。

    なるほど、一つのLINQにしてしまうのではなく、あえて分けることで整理しやすくなりますね。
    またどうしようか悩んだら、まず分けて整理してみたいと思います。

    キャンセル

0

(1) クラスBにbool HasSomeProperDescriptionOfC(ClassC needle)を作ります

public bool HasSomeProperDescriptionOfC(ClassC needle) => privateContainerOfC.Contains(needle);


※classなんでContainsが上手く動作するようIEquatable<T>を実装するなど手当してください。

(2) クラスAで、Whereをします

var thoseWhoHasClassC = this.privateContainerOfB.Where(b => b.HasSomeProperDescriptionOfC(target));

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/15 05:26

    ご回答ありがとうございます。

    なるほど、クラスB自身に探索する機能を設けても良かったのですね。
    別の機会に試してみたいと思います。

    キャンセル

同じタグがついた質問を見る

  • C#

    8507questions

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

  • for

    272questions

    for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

  • LINQ

    126questions

    LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。