c# Linq count group by - c#

My getTrustActivitiesFromStorage List looks something this
venueId venueName activityId
1 Location1 Zumba
2 Location2 Yoga
1 Location1 Yoga
1 Location1 MetaFit
3 Location3 Zumba
Here's the code i use to group etc
List<TrustActivities> filteredVenues = new List<TrustActivities>();
IEnumerable<TrustActivities> groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.Select(group => group.First())
.OrderBy(x => x.venueName);
// Loop
foreach (TrustActivities activity in groupedVenueCollection)
{
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(activity.venueId),
filterName = activity.venueName,
filterCount = 55
});
}
This successfully groups the list and outputs the 3 matches:
1 Location1 (55)
2 Location2 (55)
3 Location3 (55)
The final bit i need help with is counting each group, so filterCount = 55 will be replace with the dynamic count to give:
1 Location1 (3)
2 Location2 (1)
3 Location3 (1)
can someone show me how to do this?
thanks

You just need group.Count():
var groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.OrderBy(g => g.Key);
foreach (var group in groupedVenueCollection)
{
TrustActivities firstActivity = group.First();
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(firstActivity.venueId),
filterName = firstActivity.venueName, // or group.Key
filterCount = group.Count() // <--- !!!
});
}
You could also do it in one query without a loop:
List<TrustActivities> filteredVenues = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.OrderBy(g => g.Key)
.Select(g => new { Activity = g.First(), Count = g.Count() })
.Select(x => new TrustActivities
{
filterId = Convert.ToInt32(x.Activity.venueId),
filterName = x.Activity.venueName,
filterCount = x.Count
})
.ToList();

Instead of the .Select(g => g.First()), you'd do something like this:
IEnumerable<TrustActivities> groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => new { customer.venueId, customer.venueName });
foreach (var activity in groupedVenueCollection)
{
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(activity.Key.venueId),
filterName = activity.Key.venueName,
filterCount = activity.Count()
});
}
Also, your variable names are confusing. The table appears to be venues, but you call them customers and activities

Related

LINQ to Entities - create median value in grouped data

I have a LINQ to Entities query for grouping data and add some aggregations at the same time and it works except the median value calculation. Median value is calculated on sorted columns divided by 2 (get the middle value from the column). Here is my example:
private void button2_Click(object sender, EventArgs e)
{
var query = from t in _database.jon_export
orderby t.businessEmployeeCount
group t by t.county.ToString() into g
where g.Count() > 0
select new
{
County = g.Key,
CountValue = g.Count(),
BusinessEmployeeCount = g.Count(),
BusinessEmployeeAverageValue = g.Average(x => x.businessEmployeeCount),
//Median value from businessEmployeeCount column
BusinessRevenueAverageValue = g.Average(x => x.businessRevenue),
BusinessTurnover=g.Average(x => x.businessTurnover),
BooiqEconomicWellBeing=g.Average(x=>x.booiqEconomicWellBeing)
};
this.dataGridView1.DataSource = query.ToList();
}
Write an extension method on IEnumerable and call that extension method in the code with the right property.
public static double Median(this IEnumerable<int> items)
{
var data = items.OrderBy(n => n).ToArray();
if (data.Length % 2 == 0)
return (data[data.Length / 2 - 1] + data[data.Length / 2]) / 2.0;
return data[data.Length / 2];
}
and later in the above-mentioned snippet add as follows:
businessEmployeeMedian = g.Select(x => x.businessEmployeeCount).Median(),
For skipping businessEmployeeCount with null
businessEmployeeMedian = g.Where(x => x.businessEmployeeCount.HasValue).Select(x => (int)x.businessEmployeeCount).Median()
Complete linq in method syntax
var items = _database.jon_export
.OrderBy(item => item.businessEmployeeCount)
.GroupBy(item => item.County)
.Where(g => g.Any())
.Select(g => {
return new
{
County = g.Key,
CountValue = g.Count(),
BusinessEmployeeCount = g.Count(),
BusinessEmployeeAverageValue = g.Average(x => x.businessEmployeeCount),
businessEmployeeMedian = g.Where(x => x.businessEmployeeCount.HasValue).Select(x => (int)x.businessEmployeeCount).Median(),
BusinessRevenueAverageValue = g.Average(x => x.businessRevenue),
BusinessTurnover = g.Average(x => x.businessTurnover),
BooiqEconomicWellBeing = g.Average(x => x.booiqEconomicWellBeing)
};
}).ToList();

To Sum Value using linq

In the below code i have a list i am trying to get values from list using linq query and sum the values.But i don't know how to sum the values.So please help me to resolve the issue.
list contains:
Desc Month level value
M1 Jan L1 2
M1 Jan L2 6
M2 Feb L1 4
M2 Feb L2 1
My Expected Result:
M1 Jan 8
M2 Feb 5
var sums1 = objList
.GroupBy(y => new { y.Desc, y.Month, y.value })
.Select(group => new { KeyValue = group.Key, Count = group.Count() });
You shouldn't be including the value in your grouping - you want to group by just Desc and Month (I assume), then sum the value parts. Two options:
var sums1 = objList
.GroupBy(y => new { y.Desc, y.Month })
.Select(group => new { KeyValue = group.Key, Sum = group.Sum(y => y.value) });
Or in a single call:
var sums1 = objList.GroupBy(
y => new { y.Desc, y.Month },
(key, values) => new { key.Desc, key.Month, Sum = values.Sum(y => y.value) });
Replace your group.Count with group.Sum(x=>x.value)
Use LINQ's Sum() extension method for IEnumerable.
.Select(group => new { KeyValue = group.Key, Sum = group.Sum(obj => obj.value)});

LINQ multiple group by and then getting the first group by value count

I have a linq query like followin:
var _transactionsList = TransactionsData
.GroupBy(x => new { x.ItemID, x.Title, x.GalleryURL })
.Select(pr => new TransactionsTabResults
{
ItemID = pr.Key.ItemID,
Title = pr.Key.Title,
GalleryURL = pr.Key.GalleryURL,
ItemPrice = pr.OrderByDescending(a => a.TransactionDate).First().ItemPrice,
TotalSoldItems = pr.Count(),
TotalRevenuePerItem = pr.Sum(y => y.ItemPrice),
AveragePrice = pr.Average(y => y.ItemPrice),
}).ToList();
I'm trying to fetch the total sold items value by grouping it by like this:
ItemID Sales ItemName
1 1 Item1
1 3 Item1
1 5 Item1
1 6 Item1
2 2 Item2
2 2 Item2
2 2 Item2
2 2 Item2
The desired output would be:
ItemID Sales ItemName
1 15 Item1
2 8 Item2
The query above that I wrote gives me wrong values for total sales by saying:
TotalSoldItems = pr.Count(),
How can I count, or sum all the sales of one Item which has unique ID(this is what I'm grouping by)...
What am I doing wrong??
You are using GroubBy wrong way. You create new unique object every time. So your .GroupBy(x => new { x.ItemID, x.Title, x.GalleryURL }) and .Select(x => new { Key = new { x.ItemID, x.Title, x.GalleryURL}, Value =x }) means the same
If you need unique Id then group by Id only
TransactionsData
.GroupBy(x => x.ItemID)
.Select(pr => new TransactionsTabResults
{
ItemID = pr.Key,
Title = pr.First().Title,
GalleryURL = pr.First().GalleryURL,
ItemPrice = pr.OrderByDescending(a => a.TransactionDate).First().ItemPrice,
TotalSoldItems = pr.Count(),
TotalRevenuePerItem = pr.Sum(y => y.ItemPrice),
AveragePrice = pr.Average(y => y.ItemPrice),
}).ToList();
Advice
Optimize your LINQ. You are iterating through collections many times. This is suggested code:
TransactionsData
.GroupBy(x => x.ItemID)
.Select(pr =>
{
var items = x.pr.ToArray;
var sum = items.Sum(y => y.ItemPrice);
return new TransactionsTabResults
{
ItemID = pr.Key,
Title = items[0].Title,
GalleryURL = items[0].GalleryURL,
ItemPrice = pr.Aggregate((max, cur)=>max.TransactionDate<cur.TransactionDate?cur:max).ItemPrice,
TotalSoldItems = items.Length,
TotalRevenuePerItem = sum,
AveragePrice = sum/items.Length,
};
}).ToList();

ranking in linq based on group by condition?

I need to rank on list by Linq.
class test
{
public int Id { get; set; }
public string Destination { get; set; }
}
my data is as follows:
ID Destination
1 Miami
2 Miami
3 Boston
4 Atlanta
what i want is this:
ID Destination Value
1 Miami Miami1
2 Miami Miami2
3 Boston Boston1
4 Atlanta Atlanta1
How to get this by Linq??
Try this:
list.GroupBy(l => l.Destination)
.SelectMany(g => g.Select((x,i) => new {
x.Id,
x.Destination,
Value = x.Destination + (i+1)
}));
The i in the SelectMany will give you the index of the item in each group. Just add 1 to get the rank.
Results:
Id destination Value
1 Miami Miami1
2 Miami Miami2
3 Boston Boston1
4 Atlanta Atlanta1
Here's how you could compute the ranks within groups:
var result =
items.GroupBy(d => d.destination)
.SelectMany(g =>
g.Select(d =>
new
{
d.Id,
d.destination,
Value = d.destination +
(g.Count(x => x.Id < d.Id) + 1)
}));
var result = list.GroupBy(x=>x.Destination)
.Select(g => g.Select((x,i) =>
new {
ID = x.ID,
Destination = x.Destination,
Value = x.Destination + (i + 1)
}))
.SelectMany(x=>x);

How to select non-distinct elements along with their indexes

List<string> str = new List<string>() {
"Alpha", "Beta", "Alpha", "Alpha", "Gamma", "Beta", "XYZ" };
Expected output:
String | Indexes
----------------------------
Alpha | 0, 2, 3
Beta | 1, 5
Gamma and XYZ are distinct so, they are ignored.
I've done this by comparing the strings manually. Would it be possible to do it using LINQ in more easier way?
foreach (var grp in
str.Select((s, i) => new { s, i })
.ToLookup(pair => pair.s, pair => pair.i)
.Where(pair => pair.Count() > 1))
{
Console.WriteLine("{0}: {1}", grp.Key, string.Join(", ", grp));
}
Something like this should work:
var elements = str
.Select((Elem, Idx) => new {Elem, Idx})
.GroupBy(x => x.Elem)
.Where(x => x.Count() > 1);
If you want to get a Dictionary<string,List<int>> having the duplicated string as key and the indexes as value, just add
.ToDictionary(x => x.Key, x => x.Select(e => e.Idx).ToList() );
after Where()
You can get the non-distinct strings by grouping, then you can get the index for each non-distinct string and group them to create an array for each string:
var distinct = new HashSet<string>(
str.GroupBy(s => s)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
);
var index =
str.Select((s, i) => new {
Str = s,
Index = i
})
.Where(s => distinct.Contains(s.Str))
.GroupBy(i => i.Str).Select(g => new {
Str = g.Key,
Index = g.Select(s => s.Index).ToArray()
});
foreach (var i in index) {
Console.WriteLine("{0} : {1}", i.Str, String.Join(", ", i.Index.Select(n => n.ToString())));
}
Output:
Alpha : 0, 2, 3
Beta : 1, 5

Categories

Resources