Group by and Count on List - c#

I have a list of model class with some fields in it,I want to apply group by and count on the base of ID on it,in my lst I have data like this
lst[0]=Id=10,Name="user1",Gender="Male",Course="course1";
lst[1]=Id=10,Name="user1",Gender="Male",Course="course2";
lst[2]=Id=10,Name="user1",Gender="Male",Course="course3";
lst[3]=Id=11,Name="user2",Gender="Male",Course="course4";
I want to count the course with group by on Id.
This is how I am doing
var result = lst.GroupBy(n => n.Id).Select(c => new { Key = c.Key, total = c.Count()});
But after applying this I am not getting any value in result variable(ie result.Name or result[0].Name)
How can I apply group by and count in this list so I get the list data after applying the group by too.

Having used GroupBy you have projected (Select(..)) to an anonymous object which throws away the selected items in the group; your result will be an IEnumerable<anonymous> of items with properties Key and total
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count()});
If you want to keep the selected items, you need to include this in your projection
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count(), Items = c});
foreach(var r in result)
{
Console.WriteLine("Key: {0} Count:{1}",r.Key,r.total)
foreach(var i in r.Items)
Console.WriteLine(i.Name);
}

The reason is because you are grouping by Id and just provinding an anonymous object with Key (which is the Id) and a Count. If you want to read the values, each item of the result group set will have the Count and the values where you can loop between them. For sample:
var result = lst.GroupBy(n => n.Id);
foreach (var g in result)
{
Console.WriteLine("For group {0} the total is {1}.", g.Key, g.Count());
foreach(var item in g)
{
Console.WriteLine("Name: {0}", item.Name);
}
}

All you have to do is call ToList or ToArray.
var result = lst.GroupBy(n => n.Id)
.Select(c => new
{
Key = c.Key,
total = c.Count(),
Gender = c.First().Gender
}).ToList();
Or alternativly - to avoid multiple iterations of the same enumeration:
var result = lst.GroupBy(n => n.Id)
.Select(c =>
{
var firstEl = c.First();
return new {
Key = c.Key,
total = c.Count(),
Gender = firstEl.Gender,
Name = firstEl.Name,
Course = firstEl.Course
}
}).ToList();
Select will only return a query which is deferredly executed. This means you get the result when you actually iterate on the enumeration, which is enforeced by calling one of the mentioned methods.

Related

Group by in Linq with condition

I want to group by a table with Order Id but if one of price is negative don’t group by and brings all rows in output
I use below code but group by all order id
tblResult = tblResult.AsEnumerable().GroupBy(r => new { orderId = r["OrderID"] }).Select(g =>
{
var row = tblResult.NewRow();
row["Order ID"] = g.Key.orderId;
row["Price"] = g.Sum(r => float.Parse(r.Field<string>("Price"))).ToString();
return row;
}).CopyToDataTable();
You can create your condition in grouping, the tricky part is the result would be a list for those with negative prices and single item for those without it. if we also make single items as list then SelectMany() shoud do what you want:
var result = list.GroupBy(x => x.Id)
.SelectMany(g => g.Any(x => x.Price < 0)?
g.ToList():
new List<Order> { new Order { Id = g.Key, Price = g.Sum(grp => grp.Price)}});
LIVE DEMO

Use SelectMany to extract value from list and pass to groups

I have the following query where I use SelectMany on a list of lists and then GroupBy 'Name' and the 'Count' of the Lists and store the values in 'Name' and 'Count'.
var groups = originalList.SelectMany(fullList => fullList.ListOfItems, (fullList, details) => new { fullList.Name, fullList.ListOfItems })
.GroupBy(x => x.Name,
x => x.ListOfItems.Count())
.Select(g => new { Name = g.Key, Count = g.Count()});
//Do something with results
foreach (var item in groups)
{
var name = item.Name;
var count = item.Count;
}
Now there is another paramater from the originalList that I want to pass through to the resulting groups lets call it fullList.SomeOtherValue.
How can I modify this above so that it is passed through also?
I want to end up withthis:
foreach (var item in groups)
{
var name = item.Name;
var count = item.Count;
var someother = item.SomeOtherValue; <-- I want this as well
}
it sounds like something like this should work:
originalList.SelectMany(fullList => fullList.ListOfItems)
.GroupBy(x => new { x.Name, x.SomeOtherValue})
.Select(g => new { g.Key.Name, g.Key.SomeOtherValue, Count = g.Count()});

Getting count of duplicate items in a List [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I am trying to get the count for the most common duplicate item in a list.
So far, I have:
List<string> brandList = new List<string>();
which contains 5 different soft drink brands, and the count is 100. I need to find out which brand has the most duplicates in the list and count how many duplicates there are.
Presuming that your pseudo code actually was:
List<Brand> brandList=new List<Brand>();
// fill list
and your Brand class either overrides Equals+getHashCode or has a property like BrandID which is the identifier. N youow want to get the count of the most popular brand, you can use LINQ:
var mostPopularBrand = brandList.GroupBy(b => g.BrandID)
.OrderByDescending(g => g.Count())
.Select(g => new { BrandID = g.Key, Count = g.Count()})
.First();
Console.WriteLine("Most poular brand: {0} Count: {1}",
mostPopularBrand.BrandID, mostPopularBrand.Count);
Update: If it's actually a List<string>(question was edited):
var mostPopularBrand = brandList.GroupBy(str => str)
.OrderByDescending(g => g.Count())
.Select(g => new { Brand = g.Key, Count = g.Count()})
.First();
Your code doesn't compile. Providing that you mean List<String> as a storage of brands:
var ListbrandList = new List<String>() {
"Cola",
"Juice",
"Cola",
"Water",
"Milk",
"Water",
"Cola",
};
var result = ListbrandList
.GroupBy(item => item)
.Select(item => new {
Name = item.Key,
Count = item.Count()
})
.OrderByDescending(item => item.Count)
.ThenBy(item => item.Name);
String report = String.Join(Environment.NewLine, result
.Select(item => String.Format("{0} appears {1} time(s)", item.Name, item.Count)));
you'll have report as
Cola appears 3 time(s)
Water appears 2 time(s)
Juice appears 1 time(s)
Milk appears 1 time(s)
var result = brandList
.Distinct()
.GroupJoin(brand,
k => k,
b => b,
(k, b) => new { BrandName = k, Count = b.Count() });
// An usage could be...
foreach (var r in result)
{
Debug.WriteLine("{0} Brand has {1}", r.BrandName, r.Count);
}
Without LinQ:
var result = new Dictionary<string, int>();
foreach (var brand in brandList)
{
if (!result.ContainsKey(brand))
{
var count = brandList.FindAll(x => x.Equals(brand)).Count;
result.Add(brand, count);
}
}
foreach (var r in result)
{
Console.WriteLine("{0} Brand has {1}", r.Key, r.Value);
}
You can try to do group using LINQ by Elements and calculate count
var groupedList = from l in ListbrandList
group l by l into grp
select new { key = grp.Key, cnt = grp.Count()};
Then you will have group by key (Element) and value (Count)
Something like this maybe:
var res = brandList.GroupyBy(x => x)
.Select(x => new {Key = x.Key, Count = x.Count});
Now you have a list containing the actual drink-number and the count for this number.
EDIT: Getting the brand with most count is now easy, e.g. by using:
var mostPopular = res.Single(x => x.Count == res.Max(y => y.Count));
EDIT2: Without LINQ the following might work, which is way longer and more complicated:
// get the count for every brand
Dictionary<string, int> res = new Dictionary<string, int>();
foreach(var x in brands)
{
if (!res.ContainsKey(x)) res[x] = 0;
res[x]++;
}
// now get the max count
int currentMax = 0;
string key = "";
foreach (var kv in res)
{
if (kv.Value > currentMax)
{
currentMax = kv.Value;
key = kv.Key;
}
}
Now the key should contain the brand with highest Count.
To count element with specific value in a list use :
int quantity = lst.Count(r => r == "Cola");
Example :
List<string> lst = new List<string>()
{
"Sprite",
"Cola",
"Sprite",
"Sprite",
"Cola",
"Sprite",
"Sprite",
"Cola",
"Pepsi",
"Sprite",
"Pepsi",
"Sprite",
};
string[] popularBrands = { "Cola", "Pepsi" };
int[] quantities = new int[popularBrands.Length];
for (int i = 0; i < popularBrands.Length; i++)
{
quantities[i] = lst.Count(r => r.ToUpper() == popularBrands[i].ToUpper());
Console.WriteLine("{0} : {1}", popularBrands[i], quantities[i]);
}
Output
Cola : 3
Pepsi : 2
P.S.
About this code r => r.ToUpper() == popularBrands[i].ToUpper() :
r is variable that holds a value from our list (that are taken one by one). We also use ToUpper() to make sure that our check is case insensitive.
So we basically loop through the collection taking values out of it one by one. Each time we put value to r variable and check if this variable satisfies condition. If so - we count it, if not - we just move to next value.

Get item index of a list within Select statement

I need to index the item list with its position after grouping
var result = from i in items
group i by i.name into g
select new { groupname = g.Key,
index = //need to get the index of the item
};
How to get the item index of a list using linq/lambda?
I'm not 100% sure what you're trying to achieve, but I would definitely advice to use methods instead of syntax-based query.
var results = items.GroupBy(x => x.name)
.Select((g, i) => new { product = g.Key, index = i });
Or if you'd like to get indexes from source lift for all items within every group:
var results = items.Select((x, i) => new { x, i })
.GroupBy(x => x.x.name)
.Select(g => new {
product = g.Key,
indexes = g.Select(x => x.i).ToList()
});
var idx = 0;
var result = from i in items
group i by i.name into g
select new { product = g.Key,
index = idx++
};

Getting a distinct list with addition of the totals

I have a list of string items declared like this:
List<string> methodList = new List<string>();
I do what I need to get done and the results are something like this:
Method1;15
Method2;30
Method3;45
Method1;60
I need to loop through this list and display a distinct list and the addition of the totals. Something like this:
Method1 75
Method2 30
Method3 45
In order to do this, you'll need to split this up, then sum:
var results = methodList
.Select(l => l.Split(';'))
.GroupBy(a => a[0])
.Select(g =>
new
{
Group = g.Key,
Count = g.Count(),
Total = g.Sum(arr => Int32.Parse(arr[1]))
});
foreach(var result in results)
Console.WriteLine("{0} {1} {2}", result.Group, result.Count, result.Total);
Something like this would work:
var sumList = methodList.Select( x=>
{
var parts = x.Split(';');
return new
{
Method = parts [0],
Cost = Convert.ToInt32(parts[1])
};
})
.GroupBy( x=> x.Method)
.Select( g=> new { Method = g.Key, Sum = g.Sum( x=> x.Cost) })
.ToList();
foreach(var item in sumList)
Console.WriteLine("Total for {0}:{1}", item.Method, item.Sum);
A better approach would be to keep the individual methods and their cost in a strongly typed class, so you don't have to do string parsing to operate them:
public class MethodCost
{
public string MethodName { get; set; }
public int Cost { get; set; }
}
Now you can use a List<MethodCost> instead and have direct access to the cost - use strings for presentation (i.e. writing to the console), not for internal storage when it is not appropriate.
Try the following
var result = methodList
.Select(
x =>
{
var items = x.Split(';');
return new { Name = items[0], Value = Int32.Parse(items[1]) };
})
.GroupBy(pair => pair.Name)
.Select(
grouping =>
{
var sum = grouping.Sum(x => x.Value);
return String.Format("{0} {1}", grouping.Key, sum);
});
Yet another, slightly compacter variant is
var results = from item in methodList
let parts = item.Split(';')
group Int32.Parse(parts[1]) by parts[0];
foreach(var item in results)
Console.WriteLine("{0} {1}", item.Key, item.Sum());

Categories