Linq and group by clause - linq

I am curious what the x is for in a a linq group by clause: group x by ...
The x can be replaced by an 1:
var query = from box in c.Boxes
join item in c.Items on box equals item.Box
group 1 by new { BoxId = box.BoxId, item.ItemType.ItemTypeId } into g
select new { g.Key.BoxId, g.Key.ItemTypeId, Count = g.Count() };
Does anybody have a sample where the x (or wathever local variable you chose in the group by ) is really of some value?
I mean
var query2 = from box in c.Boxes
group box by box.BoxId into q
select q.Key;
can be replaced by
var query2 = from box in c.Boxes
group 1 by box.BoxId into q
select q.Key;

That's the expression that determines what will go in the output of the group by clause.
You are actually not inspecting the whole output, but only the Keys, which are the same in the examples above, because the grouping is done by the same thing (box.BoxId).
However, replace the last line:
select q.Key;
with
select q;
You will notice that:
the group 1 by ... query will return an IGrouping<int, int> which will have all values set to 1
the group box by ... query will return an IGrouping<int, Box>, which will have all the boxId keys and, for each of them, the corresponding Box objects with respect to the grouping criteria.

In group x by... the x is what gets aggregated.
So you could write something like
var childrenByAge = from child in class
group getName(child) by child.Age;
This gives you a grouping containing names of children for each age.
Here is a simple example where you can test the difference easily:
static void Main(string[] args)
{
var grouping = from c in "Hello World!"
group c by c; // Replace 'group c by' with 'group 1 by'
// and compare results
foreach (var group in grouping)
{
Console.Write("KEY: {0} VALUES :", group.Key);
foreach (var value in group)
Console.Write(" {0}", value);
Console.WriteLine();
}
Console.ReadKey();
}

Related

LINQ - list group by

I am having trouble with a class that has a list of objects - basically it is a shopping cart where you can add items. I wish for the items to be grouped by the item id, so they would not appear more times in the cart.
So if you add an item, and then you add it again, it should appear as 1 row, quantity =2, instead of 2 rows quantity 1.
First of all, is there a way of automatically organizing a list this way?
If not, then I need another var:
var l = from i in list.cartItems
group i by i.id into g
select g.ToList();
list.cartItems = l;
list is the object, cartItems is the list, l would be the new list to "refresh"
However, this causes me an error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable>' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
Any ideas?
Thanks
Edit:
I edited the error
Cartitems is just:
public List cartItems;
inside a CartItemList class
You should specify the grouped element you want, like this:
list.cartItems = l.First();
Your cartItems is a 'ListOfItems' type, and your var l is a new grouped type, which is 'int,ListOfItems' type. So to cast your 'int,ListOfItems' to cartItems which is 'ListOfItems', you should choose the element some element from your result collection.
for solve you need change select clause in your query - in select you can apply aggregate function like this:
var l = (from i in list.cartItems
group i by i.id into g
select new CartItem(){Id=g.Key, quantity=g.Count()}).ToList();
list.cartItems = l;
try sample that work
var t = Enumerable.Range(1, 10).Select(i => new { Id = i % 3, Q = 1 }).ToList();
var b = from i in t
group i by i.Id into g
select new { Id = g.Key, Q = g.Count() };
t = b.ToList();
and check your inputs

LINQ using Group with Count and Where, easy SQL, harder in LINQ

I'm trying to display cities names where a count is greater than 1. I can do it easy in SQL and am close in LINQ but can't figure out how to use group and also get a count and display a name
var query = (from c in Consumer
group c
by new { c.City, size = c.City.Count() }
into results
select new { Name = results.Key.City })
.Where(a => size > 0);
The size part doesn't work
try this query:
var list= Consumer.GroupBy(s=>s.City)
.Select(s=>new {
City = s.Key,
size = s.Count(),
})
.Where(s=>s.size>0).ToList();

LINQ Distinct set by column value

Is there a simple LINQ query to get distinct records by a specific column value (not the whole record)?
Anyone know how i can filter a list with only distinct values?
You could use libraries like morelinq to do this. You'd be interested in the DistinctBy() method.
var query = records.DistinctBy(record => record.Column);
Otherwise, you could do this by hand.
var query =
from record in records
group record by record.Column into g
select g.First();
Select a single value first and then run the Distinct.
(from item in table
select item.TheSingleValue).Distinct();
If you want the entire record you need to use group x by into y. You then need to find a suitable aggregate function like First, Max, Average or similar to select one of the other values in the group.
from item in table
group item by item.TheSingleValue into g
select new { TheSingleValue = g.Key, OtherValue1 = g.First().OtherValue1, OtherValue2 = g.First().OtherValue2 };
You could make an implementation of the IEqualityComparer interface:
public class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.ColumnNameProperty == y.ColumnNameProperty;
}
public int GetHashCode(MyObject obj)
{
return obj.ColumnNameProperty.GetHashCode();
}
}
And pass an instance into the Distinct method:
var distinctSource = source.Distinct(new MyObjectComparer());

Linq extract a count() value from a data object

I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });

List.GroupBy<> error using LInq

I'm trying to group a generic List<> in C#. The code compiles, but the application (Silverlight) throws the following error (CharOpps is the class of objects in the list I'm trying to group):
Unhandled Error in Silverlight Application Unable to cast object of type 'Grouping[System.DateTime,Invoc_SalesDashboard.ChartOpps]' to type 'Invoc_SalesDashboard.ChartOpps'.
Here's the code:
var newtemplist = list.GroupBy(opp =>
new DateTime(opp.EstimatedCloseDate.Year, opp.EstimatedCloseDate.Month, 1)).OrderBy(opp => opp.Key);
I've also tried:
var newtemplist =
from opp in list
orderby opp.EstimatedCloseDate
group opp by new { opp.EstimatedCloseYear, opp.EstimatedCloseMonth };
ChartOpps have a revenue value, and the EstimatedCloseDate value. What I'm hoping to end up with is a list of ChartOpps with the revenue aggregated in the year/month groupings.
foreach (ChartOpps c in newtemplist)
{
ErrorBox.Text += "o";
}
You're not showing us what you're doing with the result newtemplist. The runtime error message indicates that you are taking a group and trying to treat it as an instance of ChartOpps which is clearly impossible. Show us that code and we can help you fix it.
Edit:
Okay, now the problem is clear. To enumerate the results of the grouping, you need to proceed as follows:
foreach(var group in newtemplist) {
foreach(ChartOpps c in group) {
// do something with c here
}
}
The result of newtemplist is a sequence of sequences, each sequence having all of its elements having the same value of new DateTime(opp.EstimatedCloseDate.Year, opp.EstimatedCloseDate.Month, 1). Therefore, to enumerate this sequence of sequences, you first have to enumerate the groups, and then within each group enumerate the instances of ChartOpps.
Not knowing anything about your class structure, here is a basic attempt:
List<CharOpps> list = GetList();
var newtemplist =
from opp in list
group opp by opp.EstimatedCloseYear into g
select new { g = g.Key, CharOpps = g };
If you take the var out of the picture, it all becomes clear.
IEnumerable<IGrouping<DateTime, ChartOpps>> newtemplist = list
.GroupBy(opp => new DateTime(
opp.EstimatedCloseDate.Year,
opp.EstimatedCloseDate.Month,
1))
.OrderBy(opp => opp.Key);
foreach (ChartOpps c in newtemplist)
{
ErrorBox.Text += "o";
}
The error occurs in the assignment of the first element of newtemplist to c. c is allowed to reference ChartOpps instances. The first element of newtemplist is a IGrouping<DateTime, ChartOpps>, not a ChartOpps. The implicit cast in the foreach fails and you get a runtime exception.
Try instead:
foreach(IGrouping<DateTime, ChartOpps> g in newtemplist)
{
foreach (ChartOpps c in g)
{
ErrorBox.Text += "o";
}
}

Resources