Reputation: 11019
I am trying to better understand grouping using LINQ. I would like to take a collection of flattened records and convert them into two object collections in a parent/child relationship. In my example the flattened records are contained in ImportedExpenses class while the Expense/ExpenseLineItem classes form the parent/child relationship respectively.
public class ImportedExpense {
public string EmployeeName {get; set;}
public string CustomerName {get; set;}
public DateTime ExpenseDate {get; set;}
public Decimal Amount {get; set;}
public string Description {get; set;}
}
public class Expense {
public string EmployeeName {get; set;}
public string CustomerName {get; set;}
public List<ExpenseLineItem> LineItems {get; set;}
}
public class ExpenseLineItem {
public DateTime ExpenseDate {get; set;}
public Decimal Amount {get; set;}
public string Description {get; set;}
}
In the past I have accomplished this by repeating information in the ExpenseLineItem class.
public class ExpenseLineItem {
public string EmployeeName {get; set;}
public string CustomerName {get; set;}
public DateTime ExpenseDate {get; set;}
public Decimal Amount {get; set;}
public string Description {get; set;}
}
and I would use the following LINQ statement where importedData
is a collection of type ImportedExpense
var expenseCollection = importedData.GroupBy(x =>
new
{
x.EmployeeName,
x.CustomerName
})
.Select (y => new Expense()
{
EmployeeName = y.Key.EmployeeName,
CustomerName = y.Key.CustomerName,
LineItems = y.ToList();
});
However, I would like to accomplish the same thing without having to repeat information in the Expense and ExpenseItem class.
How would I form the LINQ query to accomplish this? If possible with the fluent syntax as I am more familiar with it vs the query syntax.
Upvotes: 0
Views: 1167
Reputation: 16564
There is a GroupBy
extension that lets you do a projection of the grouped items. You can use this to project ExpenseLineItem
s grouped by the anonymous type with the other data points in it.
In code it looks like this:
var expenseCollection = importedData
.GroupBy
(
x => new { x.EmployeeName, x.CustomerName },
x => new ExpenseLineItem
{
ExpenseDate = x.ExpenseDate,
Amount = x.Amount,
Description = x.Description
}
)
.Select
(
y => new Expense
{
EmployeeName = y.Key.EmployeeName,
CustomerName = y.Key.CustomerName,
LineItems = y.ToList()
}
);
Alternatively you can do the projection in the select:
var expenseCollection = importedData
.GroupBy(x => new { x.EmployeeName, x.CustomerName })
.Select
(
y => new Expense
{
EmployeeName = y.Key.EmployeeName,
CustomerName = y.Key.CustomerName,
LineItems = y.Select
(
item => new ExpenseLineItem
{
ExpenseDate = item.ExpenseDate,
Amount = item.Amount,
Description = item.Description
}
).ToList()
}
);
You could move the projection out to a separate method or a cast operator, but I think the above is nicely transparent.
Upvotes: 1
Reputation: 1943
I am not sure I have understood your question right, but if you want to transform ImportedExpense
to an hierarchy of Expense->ExpenseLineItem
, you are almost there with your code. Try this:
var expenseCollection = importedData.GroupBy(x =>
new
{
x.EmployeeName,
x.CustomerName
})
.Select (y => new Expense()
{
EmployeeName = y.Key.EmployeeName,
CustomerName = y.Key.CustomerName,
LineItems = y.Select(ie => new ExpenseLineItem()
{
ExpenseDate = ie.ExpenseDate,
Amount = ie.Amount,
Description = ie.Description
}).ToList();
});
Upvotes: 1