简体   繁体   中英

How to represent a conditional statement in nested foreach statements using lambda expressions in C#

I am using nested foreach statements to carry out some functionality on embedded lists ...ie class properties which are themselves classes containing lists. I would like to do this using ling and lambda expressions

Take the following method

public int GetCount1()
{

    int count1 = 0;
    int count2 = o;
    //This is the Setup code
    FirstClass fc = new FirstClass
    {
        FirstList = new List<SecondClass>
        {
            new SecondClass
            {
                SecondList = new List<ThirdClass>
                {
                    new ThirdClass
                    {
                        ThirdList = new List<FisrtStruct>
                        {
                            new FisrtStruct { int1=1, string1="one" },
                            new FisrtStruct{int1=2, string1="two" },
                            new FisrtStruct{ int1=3, string1="three" }
                        }
                    }
                }
            }
        }
    };

    foreach (var item in fc.FirstList)
    {
        foreach (var item2 in item.SecondList)
        {
            foreach (var item3 in item2.ThirdList)
            {
                if (item3.int1 > 1)
                {
                    count1++;
                }
            }
        }

    }

    // I want something along the lines of
    fc.FirstList
        .ForEach(f => 
            fc.FirstList.ForEach(fl => 
                fl.SecondList.ForEach(sl => 
                    sl.ThirdList.ForEach(tl => t1.int1 > 1 { count2++}  ))));

    return "count1@ " + count1 + " and count2: " + count2;
}

I essentially want to replicate the functionality that increments count1 on count2, but using a much smoother lambda expressions.

Below is the component classes

class FirstClass
{
    internal List<SecondClass> FirstList { get; set; }
}

class SecondClass
{
    public List<ThirdClass> SecondList { get; set; }
}

class ThirdClass
{
    internal List<FisrtStruct> ThirdList { get; set; }
}

struct FisrtStruct
{
    internal int int1 { get; set; }
    internal string string1 { get; set; }
}

Maybe it is something like this you want:

int count = fc.FirstList.SelectMany(f => f.SecondList).SelectMany(s => s.ThirdList).Count(t => t.Int1 > 1);

Maybe Rand Random was faster.

I think this will do it:

return fc.FirstList  
      .Select(i1 => i1.SecondList      //within each FirstList, look at the SecondList property
          .Select(i2 => i2.ThirdList   //within each SecondList, look at the ThirdList property
              .Count(i3 => i3.int1 > 1) //get the count for one/each ThirdList
          ).Sum()                       //sum all the ThirdList counts 
       ).Sum();                         //sum all the SecondList counts

Really, you want SelectMany() instead of Select().Sum() . This code was intended only as a bridge to build understanding what SelectMany() will do, which would have come next... but someone else posted the SelectMany() part of the solution before I got that far, and I didn't think it would be right to post basically that same code in my own answer.

I finished this much of the post because I still think it's useful to show this code as a teaching opportunity. It matches better what the loops were doing, and can therefore help build understanding for how the Linq operators and lambdas work.

Every foreach or List.ForEach can be converted to a LINQ call, either .Select() or a from in query form. List.ForEach works only for lists anyway and doesn't do anything more than a foreach

Your nested foreach calls can turn into :

var count= ( from item in fc.FirstList
             from item2 in item.SecondList
             from item3 in item2.ThirdList
             where (item3.int1 > 1)
             select item3
           ).Count();

The equivalent to nested from s is SelectMany()

var count = fc.FirstList
              .SelectMany(item=>item.SecondList)
              .SelectMany(item=>item.ThirdList)
              .Where(item=>item.int1>1)
              .Count();

Count itself can have a predicate, which saves a line of code :

var count = fc.FirstList
              .SelectMany(item=>item.SecondList)
              .SelectMany(item=>item.ThirdList)
              .Count(item=>item.int1>1);

or

var count= ( from item in fc.FirstList
             from item2 in item.SecondList
             from item3 in item2.ThirdList
             select item3
           ).Count(item3.int1 > 1);

after installing resharper for some guidance, I was able to complete my though process. Below is a complete working console program file

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetCount1());
        Console.ReadLine();

    }

static string GetCount1()
{

    int count1 = 0;
    int count2 = 0;
    int count3 = 0;

    FirstClass fc = new FirstClass
        {
            FirstList = new List<SecondClass>
            {
                new SecondClass
                {
                    SecondList = new List<ThirdClass>
                    {
                        new ThirdClass
                        {
                            ThirdList = new List<FisrtStruct>
                            {
                                new FisrtStruct { Int1= 1, String1= "one" },
                                new FisrtStruct{ Int1 = 2, String1= "two" },
                                new FisrtStruct{ Int1= 3, String1 = "three" }
                            }
                        }
                    }
                }
            }
        };

        foreach (var item in fc.FirstList)
        {
            foreach (var item2 in item.SecondList)
            {
                foreach (var item3 in item2.ThirdList)
                {
                    if (item3.Int1 > 1)
                    {
                        count1++;
                    }
                }
            }

        }


    count2 = (from item in fc.FirstList from item2 in item.SecondList from item3 in item2.ThirdList select item3).Count(item3 => item3.Int1 > 1);

    // Solution:
    fc.FirstList
        .ForEach(f => fc.FirstList
            .ForEach(fl => fl.SecondList
                .ForEach(sl => sl.ThirdList
                    .ForEach(tl =>
                    {
                        if (tl.Int1 > 1)
                        {
                            count3++;
                        }
                    }
                    ))));


    return "count1: " + count1 + " and count2: " + count2 + " and count3: " + count3;
    }


}

class FirstClass
{
    internal List<SecondClass> FirstList { get; set; }
}

class SecondClass
{
    public List<ThirdClass> SecondList { get; set; }
}

class ThirdClass
{
    internal List<FisrtStruct> ThirdList { get; set; }
}

struct FisrtStruct
{
    internal int Int1 { get; set; }
    internal string String1 { get; set; }
}

}

Hope it helps someone

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM