Reputation: 85
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
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
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
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 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);
Upvotes: 0
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