LINQ суммирует вложенные группы в одном операторе

#c# #linq

#c# #linq

Вопрос:

Я конвертирую некоторый код в LINQ, одновременно изучая, в какой степени LINQ может выполнить.

Можно ли сжать следующий код в один запрос или метод LINQ?

 Dictionary<string, ItemPack> consolidated = new Dictionary<string, ItemPack>();
foreach (var product in Products)
{
    foreach (var smallpack in product.ItemPacks)
    {
        ItemPack bigpack;
        if (consolidated.TryGetValue(smallpack.ItemCode, out bigpack))
        {
            // the big pack quantity  = quantity for making one product * the number of that product
            bigpack.Quantity  = smallpack.Quantity * product.Quantity;

            // References: we make sure that the small pack is using the Item in the big pack.
            // otherwise there will be 2 occurance of the same Item
            smallpack.Item = bigpack.Item;
        }
        else
        {
            bigpack = new ItemPack(smallpack); // Copy constructor
            bigpack.Quantity = smallpack.Quantity * product.Quantity;
            consolidated.Add(smallpack.ItemCode, bigpack);
        }
    }
}
return consolidated;
  

На английском языке каждый продукт состоит из нескольких предметов разного количества. Эти элементы сгруппированы по коду элемента и упакованы в небольшие пакеты. Эти небольшие пакеты поставляются вместе как единый продукт. Существует много разных продуктов. Один элемент может использоваться в разных продуктах.

Теперь у меня есть список продуктов и количество, необходимое для каждого для отправки. Я хочу, чтобы оператор LINQ консолидировал единый список элементов и их количества.

Я зашел так далеко, но, похоже, это не работает:

 var packsQuery = from product in Products
                 from smallpack in product.ItemPacks
                 select new {Item = smallpack.Item, Quantity = smallpack.Quantity * product.Quantity};

foreach (var pack in packsQuery)
{
    consolidated.Add(pack.Item.ItemCode, new ItemPack(pack.Item, pack.Quantity));
}
  

Если я сначала сгруппирую, я не смогу выбрать элемент по его количеству. Если я выбираю первым, я теряю группировку. История с курицей и яйцом?

РЕДАКТИРОВАТЬ: Полезное примечание: smallpack имеет тип ItemPack, который выглядит следующим образом

 public class ItemPack
{
     Item { get; } // The item in this pack, which *must* be a shared reference across all objects that uses this Item. So that change in Item properties are updated everywhere it is used. e.g. Price.
     ItemCode { get; } // The item code
     Quantity { get; } // The number of such Item in this pack.
}
  

Ответ №1:

     var query = (from product in Products
                from smallPack in product.ItemPacks
                select new
                {
                    ItemCode = smallPack.ItemCode,
                    Item = smallPack.Item,
                    Quantity = smallPack.Quantity * product.Quantity,
                })
                .GroupBy(p => p.ItemCode)
                .Select(p => new
                {
                    ItemCode = p.Key,
                    Item = p.FirstOrDefault(),
                    Quantity = p.Sum(x=>x.Quantity)
                })
                .ToDictionary(p=>p.ItemCode);
  

Ответ №2:

Спасибо, что указали мне правильное направление. Мне удалось разработать полную версию синтаксиса запроса:

 var query = from product in Products
            from smallpack in product.ItemPacks
            select new {
                Item = smallpack.Item,
                Quantity = smallpack.Quantity * product.Quantity
            } into mediumpack
            group mediumpack by mediumpack.Item.ItemCode into bigpack
            select new {
                Item = bigpack.First().Item, // shared reference
                Quantity = bigpack.Sum(a => a.Quantity);
            }

query.ToDictionary(...);
  

Любые комментарии относительно того, хорошо ли это?

Комментарии:

1. Да, это то же самое, что и у меня, так что все в порядке.