Linq left join with multiple columns and default values [duplicate] - linq

This question already has answers here:
Closed 10 years ago.
I am fairly new to linq and I need to join two tables with the following requirements:
Should left join t1 and t2.
If t2 is empty then the query should not fail - should use default values.
My query:
var final = from t1 in saDist.AsEnumerable()
from t2 in sapGrouped.AsEnumerable()
where
t1.Supplier.Id == t2.Supplier.Id && t1.VatRate == t2.VatRate
select
new
{
t1.Supplier,
Amount = t1.Amount - t2.Amount,
Advance = t1.Advance - t2.Advance,
Balance = t1.Balance - t2.Balance,
t1.VatRate
};
Can someone correct this?

This works in Linqpad as a C# program.
Basically your join syntax needed tweaking (see this), and you needed to take into account when there was nothing to join to for "t2" (so we do a null check and use 0 when null, otherwise t2.Amount, etc)
I created some dummy data so you can play around.
See http://codingsense.wordpress.com/2009/03/08/left-join-right-join-using-linq/ for another example.
I hope it does what you want it to do.
Thanks,
Dominique
public class A
{
void Main()
{
Distributor dist1 = new Distributor() { SupplierID = 1, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "A", DeptSupplierID = 1 };
Distributor dist2 = new Distributor() { SupplierID = 2, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "B", DeptSupplierID = 1 };
Distributor dist3 = new Distributor() { SupplierID = 3, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "C", DeptSupplierID = 1 };
Distributor dist4 = new Distributor() { SupplierID = 4, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "D", DeptSupplierID = 2 };
Distributor dist5 = new Distributor() { SupplierID = 5, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "E", DeptSupplierID = 2 };
Distributor dist6 = new Distributor() { SupplierID = 6, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "F", DeptSupplierID = 2 };
Distributor dist7 = new Distributor() { SupplierID = 7, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "G", DeptSupplierID = 6 };
Distributor dist8 = new Distributor() { SupplierID = 8, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "H", DeptSupplierID = 3 };
Distributor dist9 = new Distributor() { SupplierID = 9, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "I", DeptSupplierID = 3 };
Distributor dist10 = new Distributor() { SupplierID = 10, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "J", DeptSupplierID = 7 };
Distributor dist11 = new Distributor() { SupplierID = 11, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "K", DeptSupplierID = 7 };
Distributor dist12 = new Distributor() { SupplierID = 12, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "L", DeptSupplierID = 5 };
SAPGroup Dept1 = new SAPGroup() { SupplierID = 1, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "Development" };
SAPGroup Dept2 = new SAPGroup() { SupplierID = 2, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "Testing" };
SAPGroup Dept3 = new SAPGroup() { SupplierID = 3, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "Marketing" };
SAPGroup Dept4 = new SAPGroup() { SupplierID = 4, Amount = 3, Balance = 4, Advance = 3, VatRateID = 1, Name = "Support" };
List ListOfDistributors = new List();
ListOfDistributors.AddRange((new Distributor[] { dist1, dist2, dist3, dist4, dist5, dist6, dist7,
dist8, dist9, dist10, dist11, dist12 }));
List ListOfSAPGroup = new List();
ListOfSAPGroup.AddRange(new SAPGroup[] { Dept1, Dept2, Dept3, Dept4 });
var final = from t1 in ListOfDistributors
join t2 in ListOfSAPGroup
on new { t1.SupplierID, t1.VatRateID } equals new { t2.SupplierID, t2.VatRateID }
into JoinedDistAndGrouped
from t2 in JoinedDistAndGrouped.DefaultIfEmpty()
select new
{
Name1 = t1.Name,
Name2 = (t2 == null) ? "no name" : t2.Name,
SupplierID = t1.SupplierID,
Amount = t1.Amount - (t2 == null ? 0 : t2.Amount),
Advance = t1.Advance - (t2 == null ? 0 : t2.Advance),
Balance = t1.Advance - (t2 == null ? 0 : t2.Balance),
VatRateID = t1.VatRateID
};
final.Dump();
}
}
class Distributor
{
public string Name { get; set; }
public int SupplierID { get; set; }
public int VatRateID { get; set; }
public int DeptSupplierID { get; set; }
public int Amount { get; set; }
public int Advance { get; set; }
public int Balance { get; set; }
}
class SAPGroup
{
public int SupplierID { get; set; }
public int VatRateID { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public int Advance { get; set; }
public int Balance { get; set; }
}
public class Result
{
public string Name1 { get; set; }
public string Name2 { get; set; }
public int SupplierID { get; set; }
public int Amount { get; set; }
public int Advance { get; set; }
public int Balance { get; set; }
public int VatRateID { get; set; }
}

Thanks for your input. None of the answers did quite what I wanted, but I managed to get my original code working:
var final = from t2 in saDist.AsEnumerable()
from t1 in sapGrouped.AsEnumerable().DefaultIfEmpty()
where
t1 == null || (t2.Supplier.Id == t1.Supplier.Id && t2.VatRate == t1.VatRate)
select
new
{
t2.Supplier,
Amount = t2.Amount - (t1 == null ? 0 : t1.Amount),
Advance = t2.Advance - (t1 == null ? 0 : t1.Advance),
Balance = t2.Balance - (t1 == null ? 0 : t1.Balance),
t2.VatRate
};
If you have any comments or improvements on this let me know, thanks.

According to this, you are looking for something like (this is untested, but hopefully leads you on the right track):
var final = from t1 in saDist.AsEnumerable()
join t2 in sapGrouped.AsEnumerable()
on t1.Supplier.Id equals t2.Supplier.Id
and t1.VatRate equals t2.VatRate into t1_t2 //not sure about this line
from t2 in t1_t2.DefaultIfEmpty()
{
t1.Supplier,
Amount = t1.Amount - t2.Amount,
Advance = t1.Advance - t2.Advance,
Balance = t1.Balance - t2.Balance,
t1.VatRate
};
Notice the .DefaultIfEmpty(), this satisfies: "If t2 is empty then the query should not fail - should use default values."

Related

GroupBy using LINQ but base distinct on another column, and still be able to count other records

How should I run a GroupBy based on Id using LINQ when there is an object similar to following:
public class foo
{
public int id { get; set; }
public string name { get; set; }
public string lang { get; set; }
public int displayOrder { get; set; }
public int count { get; set; }
}
The list could be:
id = 1, name="test1", lang = "en", displayOrder = 1, count = 1
id = 1, name="test2", lang = "fr", displayOrder = 2, count = 2
id = 1, name="test3", lang = "de", displayOrder = 3, count = 1
id = 2, name="test4", lang = "en", displayOrder = 2, count = 1
id = 2, name="test5", lang = "fr", displayOrder = 3, count = 1
id = 3, name="test6", lang = "en", displayOrder = 6, count = 1
id = 3, name="test7", lang = "fr", displayOrder = 4, count = 1
id = 4, name="test8", lang = "en", displayOrder = 5, count = 1
id = 5, name="test9", lang = "de", displayOrder = 6, count = 1
I want to run LINQ so that it Groups By Id values, but the distinct id values should be filtered based on lang e.g. "fr", if nothing is available in "fr" it should output only default language record for "en"
but should also Count the total number of records based on Id, it should retrieve following results for above:
id = 1, name="test2", lang = "fr", displayOrder = 2, count = 4
id = 2, name="test5", lang = "fr", displayOrder = 3, count = 2
id = 3, name="test7", lang = "fr", displayOrder = 4, count = 2
id = 4, name="test8", lang = "en", displayOrder = 5, count = 1
id = 5, name="test9", lang = "de", displayOrder = 6, count = 1
Please, is there a way to do something like this using LINQ ?
All of you LINQ experts, I'm ideally looking for query using lambda, this would be a great help. Thanks in advance.
You can sort the target language to the front, and select the first item in a group:
var query = from f in foos
group f by f.id into g
let lang = (from f in g
orderby
f.lang == "fr" ? 0 : 1,
f.lang == "en" ? 0 : 1,
f.lang
select f).First()
select new
{
id = g.Key,
lang.name,
lang.lang,
lang.displayOrder,
count = g.Sum(f => f.count)
};
This assumes that pairs (id, lang) are unique.
Demo.
This may run slightly faster as it does not need to go through all items in the group for ordering.
var res = data
.GroupBy(d => d.id)
.Select(g => new {
id = g.Key,
count = g.Sum(d => d.count),
Lang = g.FirstOrDefault(d => d.lang == "fr") ?? g.First()
})
.Select(g => new {
g.id,
g.Lang.lang,
g.Lang.name,
g.Lang.displayOrder,
g.count
})
.ToList();

How to get maximum Id in List and then return list

I have a structure like this:
No ID Name Status
1 1 A 1
2 1 B 1
3 1 c 1
4 1 D 1
5 2 E 3
6 2 F 3
7 2 G 3
I want to run a linq when I get a list results get maximum row where for each status and row details as well.Like:
No ID Name Status
4 1 D 1
7 2 G 3
Means latest entry for the status.
Is there a way around, as I have tried all Max, Orderby descending but I get single result but I need a List as a result.
You have to extract the groups of a same id (GroupBy), and then export the max No for each group, with a SelectMany :
public void Exec()
{
var items = new List<Item>{
new Item{ No = 1, Id = 1, Name = "A", Status = 1} ,
new Item{ No = 2, Id = 1, Name = "B", Status = 1} ,
new Item{ No = 3, Id = 1, Name = "C", Status = 1} ,
new Item{ No = 4, Id = 1, Name = "D", Status = 1} ,
new Item{ No = 5, Id = 2, Name = "E", Status = 1} ,
new Item{ No = 6, Id = 2, Name = "F", Status = 1} ,
new Item{ No = 7, Id = 2, Name = "G", Status = 1} ,
};
var result = items
.GroupBy(groupedItems => groupedItems.Id)
.SelectMany(i => items
.Where(innerItem => innerItem.Id == i.Key && innerItem.No == i.Max(ii => ii.No))
.Select(innerItem => innerItem)
);
foreach (var item in result)
Console.WriteLine("Max item of Id {0} : No = {1}, Name = {2}, Status = {3}", item.Id, item.No, item.Name, item.Status);
}
private class Item
{
public Int32 No { get; set; }
public Int32 Id { get; set; }
public String Name { get; set; }
public Int32 Status { get; set; }
}
output :
Max item of Id 1 : No = 4, Name = D, Status = 1
Max item of Id 2 : No = 7, Name = G, Status = 1
Alternative:
items.GroupBy(groupedItems => groupedItems.Id)
.Select(g => g.OrderByDescending(x => x.No).First())

PIVOT with LINQ from Datatable [duplicate]

I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}

Linq subselect filter

probably someone can help me with this (at least for me) complicated problem.
Lets say i have the following data (in DB)
Tab1 (id_t1): Item
(1)
(2)
(3)
Tab2 (id_t2, id_t1): Group
(4, 1)
(5, 1)
(6, 2)
(7, 3)
Tab3 (id_t3, id_t2, v): GroupField
(10, 4, 100)
(11, 4, 300)
(12, 5, 200)
(13, 6, 100)
(14, 6, 200)
(15, 7, 100)
(16, 7, 300)
Now i'd like to select all Items that include all of some specific GroupFields.
Eg. i have v = list(100,200)
and i like to get back 1,2 but not 3
1 because Group4 holds the Field10 with v=100 and Group5 holds Field12 with v=200
and 2 because Group6 holds Field13 with v=100 and Field14 with v=200
Is something like this possible in Linq? (i allready tried different ways (any/all) but without success so far.
I don't get the point how to overcome that "field can be in any Group and not all in one Group"...
I don't even know how to do this in SQL in one command without using temp-tables/cursors.
_rene
Try this:
var result =
groups.Join(fields, o => o.Id, i => i.GroupId,
(o, i) => new { Group = o, Field = i } )
.GroupBy(x => x.Group.ItemId)
.Where(x => values.All(y => x.Any(z => z.Field.Value == y)))
.Select(x => x.Key)
.Distinct();
The following classes are used:
class Group
{
public Group(int id, int itemId)
{
Id = id;
ItemId = itemId;
}
public int Id { get; set; }
public int ItemId { get; set; }
}
class GroupField
{
public GroupField(int id, int groupId, int value)
{
Id = id;
GroupId = groupId;
Value = value;
}
public int Id { get; set; }
public int GroupId { get; set; }
public int Value { get; set; }
}
and the following initialization:
var groups = new [] { new Group(4, 1), new Group(5, 1),
new Group(6, 2), new Group(7, 3) };
var fields = new [] { new GroupField(10, 4, 100),
new GroupField(11, 4, 300),
new GroupField(12, 5, 200),
new GroupField(13, 6, 100),
new GroupField(14, 6, 200),
new GroupField(15, 7, 100),
new GroupField(16, 7, 300)
};
var values = new [] { 100, 200 };

How do I transfer this logic into a LINQ statement?

I can't get this bit of logic converted into a Linq statement and it is driving me nuts. I have a list of items that have a category and a createdondate field. I want to group by the category and only return items that have the max date for their category.
So for example, the list contains items with categories 1 and 2. The first day (1/1) I post two items to both categories 1 and 2. The second day (1/2) I post three items to category 1. The list should return the second day postings to category 1 and the first day postings to category 2.
Right now I have it grouping by the category then running through a foreach loop to compare each item in the group with the max date of the group, if the date is less than the max date it removes the item.
There's got to be a way to take the loop out, but I haven't figured it out!
You can do something like that :
from item in list
group item by item.Category into g
select g.OrderByDescending(it => it.CreationDate).First();
However, it's not very efficient, because it needs to sort the items of each group, which is more complex than necessary (you don't actually need to sort, you just need to scan the list once). So I created this extension method to find the item with the max value of a property (or function) :
public static T WithMax<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector)
{
var max = default(TValue);
var withMax = default(T);
var comparer = Comparer<TValue>.Default;
bool first = true;
foreach (var item in source)
{
var value = selector(item);
int compare = comparer.Compare(value, max);
if (compare > 0 || first)
{
max = value;
withMax = item;
}
first = false;
}
return withMax;
}
You can use it as follows :
from item in list
group item by item.Category into g
select g.WithMax(it => it.CreationDate);
UPDATE : As Anthony noted in his comment, this code doesn't exactly answer the question... if you want all items which date is the maximum of their category, you can do something like that :
from item in list
group item by item.Category into g
let maxDate = g.Max(it => it.CreationDate)
select new
{
Category = g.Key,
Items = g.Where(it => it.CreationDate == maxDate)
};
How about this:
private class Test
{
public string Category { get; set; }
public DateTime PostDate { get; set; }
public string Post { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
List<Test> test = new List<Test>();
test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 5, 12, 0, 0), Post = "A1" });
test.Add(new Test() { Category = "B", PostDate = new DateTime(2010, 5, 5, 13, 0, 0), Post = "B1" });
test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 12, 0, 0), Post = "A2" });
test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 13, 0, 0), Post = "A3" });
test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 14, 0, 0), Post = "A4" });
var q = test.GroupBy(t => t.Category).Select(g => new { grp = g, max = g.Max(t2 => t2.PostDate).Date }).SelectMany(x => x.grp.Where(t => t.PostDate >= x.max));
}
Reformatting luc's excellent answer to query comprehension form. I like this better for this kind of query because the scoping rules let me write more concisely.
from item in source
group item by item.Category into g
let max = g.Max(item2 => item2.PostDate).Date
from item3 in g
where item3.PostDate.Date == max
select item3;

Resources