How to use Linq instead of foreach to Group and Count - c#

I have 2 Lists, one for Players and another for Coaches.
To count number of Players belong to a Coach, I used:
var g = AllePlayer.GroupBy(play=> play.coachNumer);
foreach (var grp in g)
{
foreach (Coach co in AlleCoaches)
{
if (co.coachNumer== grp.Key)
{
co.NumOfPlayer= grp.Count();
}
}
}
Now I want to know if there is a nice way to put the lover parts with "foreach" in a nice Linq syntax to avoid this "foreach" loops.
Thank you in advance!

You could change the statement around a little bit. Since ultimately you want to change a property on each coach, it's easiest to loop through that list, as in:
foreach (Coach co in AlleCoaches)
{
co.NumOfPlayer= AllePlayer.Where(p => p.coachNumber == co.coachNumber)
.Count();
}

AlleCoaches.ToList()
.ForEach(n=>n.NumOfPlayer=AllePlayer.Where(n=>coachNumer==n.coachNumer).Count());

This would be a simpler approach:
var query = AllePlayer.GroupBy(player => player.coachNumer,
(coach, players => new {
Coach = coach,
Count = players.Count() }));
That will give you a sequence where each element is the coach and the number of players for that coach.
Then you could iterate over the result, and assign the value into Coach.NumOfPlayer, but do you really need to? If you do, this will do it:
foreach (var pair in query)
{
pair.Coach.NumOfPlayer = pair.Count;
}
Personally it doesn't feel like "number of players" should be part of the Coach type to start with...

var g = AllePlayer.TakeWhile(play=> ( play.coachNumer!=null));
You may take whatever logic you need but syntax would be the same.
var count = g.Count();

Related

Can I convert Dictionary loop inside of a foreach loop into a LINQ statement

If possible please help me convert these nested loops into a LINQ statement
Thank you very much for your help!
public static List<Term> GetTermsByName(this IEnumerable<Term> terms, Dictionary<string, string> termInfo)
{
List<Term> termList = new List<Term>();
foreach (Term term in terms)
{
foreach (var value in termInfo.Values)
{
if (term.Name == value)
{
termList.Add(term);
}
}
}
return termList;
}
Maybe Contains method is what you are after
Determines whether a sequence contains a specified element.
The following can be read as, Filter all Terms where the Term.Name exists in the dictionary Values
var values = termInfo.Values;
var result = terms.Where(term => values.Contains(term.Name));
.ToList();
// or
var result = terms.Where(term => termInfo.Values.Contains(term.Name));
.ToList();
You're losing the plot of the dictionary a bit here, don't you think? The speediness is in using the keys. However, you can still do better than a nested foreach or an inline linq equivalent with where and contains. Use a join to at least improve your efficiency.
var termList = (from term in terms
join value in termInfo.Values
on term.Name equals value
select term)
.Distinct() // if there are duplicates in either set
.ToList();

List of Objects with int property compared to List of Int

I have 2 lists. First is a list of objects that has an int property ID. The other is a list of ints.
I need to compare these 2 lists and copy the objects to a new list with only the objects that matches between the two lists based on ID. Right now I am using 2 foreach loops as follows:
var matched = new list<Cars>();
foreach(var car in cars)
foreach(var i in intList)
{
if (car.id == i)
matched.Add(car);
}
This seems like it is going to be very slow as it is iterating over each list many times. Is there way to do this without using 2 foreach loops like this?
One slow but clear way would be
var matched = cars.Where(car => intList.Contains(car.id)).ToList();
You can make this quicker by turning the intList into a dictionary and using ContainsKey instead.
var intLookup = intList.ToDictionary(k => k);
var matched = cars.Where(car => intLookup.ContainsKey(car.id)).ToList();
Even better still, a HashSet:
var intHash = new HashSet(intList);
var matched = cars.Where(car => intHash.Contains(car.id)).ToList();
You could try some simple linq something like this should work:
var matched = cars.Where(w => intList.Contains(w.id)).ToList();
this will take your list of cars and then find only those items where the id is contained in your intList.

Best way to write this in Linq

How do I refactor this in LINQ. I am working towards embracing good coding practices. My tds object looks like this:
tds -> BuildingName(List) -> buildingFloor(string)
What I am accomplishing with the nested foreach loop is reading the buildingFloors into a list.
List<string> bname = new List<string>();
foreach (var building in tds) {
foreach (var x in building.BuildingName) {
bname.Add(x);
}
}
You can do it with SelectMany which will flatten your items
List<string> bname = tds.SelectMany(b => b.BuildingName).ToList();
Use Enumerable.SelectMany to flatten your list like:
List<string> bname = tds.SelectMany(r=> r.BuildingName).ToList();
What you're looking for is SelectMany.
var bname = buildings.SelectMany(b => b.BuildingName);
Note, if you're going to be enumerating over the result multiple times with foreach then you will probably want to ToList() it, so you have a hard list rather than an enumerable that is executed every time you enumerate it.
var bname = buildings.SelectMany(b => b.BuildingName).ToList();
something like this I suppose:
var bname = tds.SelectMany(x => x.BuildingName);
And if you need a List call .ToList() at the end
var bname = tds.Select( x=> x.buildingName).ToList() ;
edit: I did not see the inner foreach while on my commute to work, it is indeed
var bname = tds.SelectMany(x=> x.BuildingName).ToList();
using query-syntax:
List<string> bname =
(from building in tds
from x in building.BuildingName
select x)
.ToList()

Simplifying linq when filtering data

I wanted to ask for suggestions how I can simplify the foreach block below. I tried to make it all in one linq statement, but I couldn't figure out how to manipulate "count" values inside the query.
More details about what I'm trying to achieve:
- I have a huge list with potential duplicates, where Id's are repeated, but property "Count" is different numbers
- I want to get rid of duplicates, but still not to loose those "Count" values
- so for the items with the same Id I summ up the "Count" properties
Still, the current code doesn't look pretty:
var grouped = bigList.GroupBy(c => c.Id).ToList();
foreach (var items in grouped)
{
var count = 0;
items.Each(c=> count += c.Count);
items.First().Count = count;
}
var filtered = grouped.Select(y => y.First());
I don't expect the whole solution, pieces of ideas will be also highly appreciated :)
Given that you're mutating the collection, I would personally just make a new "item" with the count:
var results = bigList.GroupBy(c => c.Id)
.Select(g => new Item(g.Key, g.Sum(i => i.Count)))
.ToList();
This performs a simple mapping from the original to a new collection of Item instances, with the proper Id and Count values.
var filtered = bigList.GroupBy(c=>c.Id)
.Select(g=> {
var f = g.First();
f.Count = g.Sum(c=>c.Count);
return f;
});

How to get the re-occurrence count of an item in an IList? (Without Immutable in loop)

Hey guys I feel like this is a pretty simple question but I cannot find the answer. I have an IList with a bunch of items in it. And I want to count how many items are duplicated(This IList will have a lot of duplicated). So if the list has 'apple' in it 20 times and 'bannana' in it 30 times I want to put those into an associative array with the name as the key and the count as the value. I am currently doing it with this....
var summary = new Dictionary<string,int>();
foreach (myModel.Row in model.items)
{
if (summary.Count == 0 || !summary.ContainsKey(row.ItemTitle))
{
summary.Add(row.ItemTitle, 1);
}
else
{
summary[row.ItemTitle] += 1;
}
}
so model.items is an IList that contains Row.
The problem with this is that in the else I am incrementing an immutable in a loop, so I know there is a better way.
Thanks in advance for the help.
edit
What I mean by no immutable in a loop is I am trying to avoid this summary[row.ItemTitle] += 1; and the negative performance implications of it. If there are any.
You can use LINQ GroupBy to get the result:
var summary = model.items.GroupBy(x => x.ItemTitle)
.ToDictionary(g => g.Key, g => g.Count());

Categories

Resources