Итак, у вас есть две последовательности: последовательность Balances
и последовательность Sales
.
Каждый Balance
имеет как минимум свойство int CustomerId
и BalanceValue
Каждый Sale
имеет по крайней мере обнуляемое свойство int CustomerId и свойство DateTime SaleDate
class Balance
{
public int CustomerId {get; set;}
public decimal BalanceValue {get; set;}
}
class Sale
{
public int? CustomerId {get; set;}
public DateTime SaleDate {get; set;}
}
Очевидно, что продажа возможна без покупателя.
Теперь, учитывая последовательность Balances
и Sales
, вы хотите для каждого Balance
одного объекта, содержащего CustomerId
, BalanceValue
и SaleDate
, которые этот Клиент имеет в последовательности Sales
.
В вашем требовании не указано, что вы хотите с Балансами в вашей последовательности Балансов, у которых есть Клиент без Продаж в последовательности Продаж.Предположим, вы хотите, чтобы эти элементы в вашем результате имели нулевое значение LastSaleDate
.
IEnumerable<Balance> balances = ...
IEnumerable<Sales> sales = ...
. Мы не заинтересованы в продажах без покупателя.Итак, давайте сначала отфильтруем их:
var salesWithCustomers = sales.Where(sale => sale.CustomerId != null);
Теперь создадим группы сальдо с их продажами по groupjoiningon CustomerId:
var balancesWithSales = balances.GroupJoin(salesWithCustomers, // GroupJoin balances and sales
balance => balance.CustomerId, // from every Balance take the CustomerId
sale => sale.CustomerId, // from every Sale take the CustomerId. I know it is not null,
(balance, salesOfBalance) => new // from every balance, with all its sales
{ // make one new object
CustomerId = balance.CustomerId,
Balance = balance.BalanceValue,
LastSaleDate = salesOfBalance // take all salesOfBalance
.Select(sale => sale.SaleDate) // select the saleDate
.OrderByDescending(sale => sale.SaleDate) // order: newest dates first
.FirstOrDefault(), // keep only the first element
})
Расчет LastSaleDate
упорядочивает все элементы, идержит только первое.
Хотя это решение работало, неэффективно заказывать все остальные элементы, если вам нужен только самый большой элемент.Если вы работаете с IEnumerables, а не с IQueryables (как в базе данных), вы можете оптимизировать это, создав функцию.
Я реализую это как функцию расширения.См. демистифицированные методы расширения
static DateTime? NewestDateOrDefault(this IEnumerable<DateTime> dates)
{
IEnumerator<DateTime> enumerator = dates.GetEnumerator();
if (!enumerator.MoveNext())
{
// sequence of dates is empty; there is no newest date
return null;
}
else
{ // sequence contains elements
DateTime newestDate = enumerator.Current;
while (enumerator.MoveNext())
{ // there are more elements
if (enumerator.Current > newestDate)
newestDate = enumerator.Current);
}
return newestDate;
}
}
Использование:
LastSaleDate = salesOfBalance
.Select(sale => sale.SaleDate)
.NewesDateOrDefault();
Теперь вы знаете, что вместо сортировки полной последовательности вы будете перечислять последовательность только один раз.
Примечание: вы не можете использовать Enumerable.Aggregate для этого, потому что он не работает с пустыми последовательностями.