Roland
Roland

Reputation: 85

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; }
}

Upvotes: 0

Views: 179

Answers (4)

Roland
Roland

Reputation: 85

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

Upvotes: 0

Joel Coehoorn
Joel Coehoorn

Reputation: 415765

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.

Upvotes: 0

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131374

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 froms 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);

Upvotes: 0

Casperah
Casperah

Reputation: 4554

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.

Upvotes: 1

Related Questions