This is my first question in this community and I would like to get an answer to my problem , Suppose that I have two lists A and B:
List<int> listA = new List<int>();
List<int> listB = new List<int>();
listA.Add(1);
listA.Add(2);
listA.Add(8);
listB.Add(1);
listB.Add(1);
listB.Add(1);
listB.Add(2);
listB.Add(2);
listB.Add(2);
listB.Add(2);
I would like to count the occurrences of each element in listB that already existed in listA and add zero if does not exist:
This is what I've tried:
var result = listB.GroupBy(x => x).ToDictionary(k => k.Key, v => v.Count());
foreach(var res in result.Values)
Console.WriteLine(res); // it will print only {3,4}
The expected result will be :
// { 3,4,0} => 3 is the occurrences number of 1 , 4 is the occurrences number of 2 ,and 0 is the number occurrences of 8
How can I achieve this result?
CodePudding user response:
I would use the linq .Count() extension, which will count the number of items that satisfy a condition. Yes, this will iterate the list more times than is necessary, but it doesn't create any unnecessary objects, and it is very readable:
var countOccurences = new List<int>();
foreach (var inA in listA)
{
countOccurences.Add(listB.Count(inB => inB == inA));
}
Console.WriteLine(string.Join(", ", countOccurences));
Once you've got the loop working, then it should be easy to see that it can be done in a single statement:
var countOccurences = listA.Select(inA => listB.Count(inB => inA == inB));
CodePudding user response:
fixing your code
var result = listB.GroupBy(x => x).ToDictionary(k => k.Key, v => v.Count());
foreach(var ent in listA)
Console.WriteLine(result.ContainsKey(ent)?result[ent]:0);
CodePudding user response:
one line Linq statment
listA.Select(a => listB.Contains(a) ? a : 0 ).Select(r=>listB.Count(w=> w==r)).ToList();
CodePudding user response:
It's a good problem to use left outer join in LINQ. Here is an article about it. It's the best performance solution because you iterate over the collection just once and it's important for big collections or frequently used methods.
Here is an example for your problem:
var result = from a in listA
join b in listB.GroupBy(x => x) on a equals b.Key into gj
from subList in gj.DefaultIfEmpty()
select new
{
Number = a,
Count = subList?.Count() ?? 0
};
You can also use GroupJoin and method chaining but I think that it's much harder to read:
var result = listA
.GroupJoin(listB.GroupBy(x => x), a => a, b => b.Key, (a, gj) => new { a, gj })
.SelectMany(@t => @t.gj.DefaultIfEmpty(), (@t, subList) => new { Number = @t.a, Count = subList?.Count() ?? 0 });
After that result will contains a collection of anonymous types with fields Number
and Count
with 3 elements: {1, 3}, {2, 4}, {8, 0}
.
Update from comments (thanks to Caius Jard): if you can't use Null-Conditional Operator you may just replace it with explicit check: subList == null ? 0 : subList.Count()