Манипулировать коллекцией для создания сжатой версии

76
11

Я в настоящее время тупик на небольшой функциональности, которую я не могу понять.

Во-первых, у меня есть класс Stock который выглядит следующим образом:

public class Stock
{
public int Id;
public int LocationId;
public int Quantity;
}

Stock журналы возвращаются из базы данных, которые производятся из другой части функциональности. Журнал представлен как коллекция List<Stock> но мне нужно добавить свойство Quantity каждого объекта с одинаковой ID и ID LocationID, например:

Набор исходных данных:

ID: 1 Расположение: 1 Количество: 20

ID: 1 Расположение: 2 Количество: 30

ID: 1 Расположение: 1 Количество: 30

ID: 2 Расположение: 2 Количество: 20

ID: 1 Расположение: 2 Количество: 30

ID: 1 Расположение: 1 Количество: 100

Должен вернуться:

Набор сжатых данных:

ID: 1 Расположение: 1 Количество: 150

ID: 1 Расположение: 2 Количество: 60

ID: 2 Расположение: 2 Количество: 20

Повторить: набор данных динамически возвращается из базы данных, нет гарантии, что будет каждая комбинация ID и LocationID и мне нужно, чтобы результирующий набор данных был уникальным на составном ключе ID и LocationID.

Не уверенный в наиболее эффективном способе обойти это, и это сдерживает мой прогресс в проекте, любые советы или подходы будут высоко оценены. Я как бы думаю, что это действительно пробел в знаниях, но я не смог найти что-либо подходящее/отвечающее моим требованиям (это довольно странное требование, я думаю).

Большое спасибо,

Энди

спросил(а) 2021-01-19T21:04:41+03:00 2 месяца, 3 недели назад
1
Решение
75

Было бы лучше сделать это в базе данных, но вы также можете использовать GroupBy для достижения такого же эффекта:

public class Stock
{
public int Id;
public int LocationId;
public int Quantity;
}

static void Main(string[] args)
{
var list = new List<Stock>()
{
new Stock(){ Id = 1, LocationId = 1, Quantity = 20},
new Stock(){ Id = 1, LocationId = 2, Quantity = 30},
new Stock(){ Id = 1, LocationId = 1, Quantity = 30},
new Stock(){ Id = 2, LocationId = 2, Quantity = 20},
new Stock(){ Id = 1, LocationId = 2, Quantity = 30},
new Stock(){ Id = 1, LocationId = 1, Quantity = 100},

};

var grouped = list.GroupBy(c => new {Id = c.Id, LocationId = c.LocationId})
.Select(g => new
{
Id = g.Key.Id,
LocationId = g.Key.LocationId,
Quantity = g.Sum(a => a.Quantity)
});
foreach(var group in grouped.OrderBy(c => c.Id))
{
Console.WriteLine("Id:{0} - LocationId:{1} - Quantity:{2}", group.Id,
group.LocationId, group.Quantity);
}
}

ответил(а) 2021-01-19T21:04:41+03:00 2 месяца, 3 недели назад
76

Используйте GroupBy для этого:


var grouped = (from s in stocks
group s by new { s.Id, s.LocationId }
into grp
select new Stock()
{
Id = grp.Key.Id,
LocationId = grp.Key.LocationId,
Quantity = grp.Sum(x => x.Quantity)
}).ToList();

ответил(а) 2021-01-19T21:04:41+03:00 2 месяца, 3 недели назад
61

Я предпочту использовать SQL-запрос примерно так:

select id, location, sum(quantity) quant from stocktable group by id, location

Это помогает получить вычисления, выполненные в db, которые помогают вам с точки зрения производительности. Поскольку сервер БД в любом случае будет читать и предоставлять все данные на уровне приложения, нет никакого штрафа за производительность, и вы получите с точки зрения простоты.

ответил(а) 2021-01-19T21:04:41+03:00 2 месяца, 3 недели назад
44

Вы можете использовать Enumerable.GroupBy для выполнения группировки и Enumerable.Aggregate (или в этом случае специализированной Sum) для выполнения агрегации.

Что-то вроде:

IEnumerable<Tuple<int, int, int>> result =
stocks.GroupBy(stock => new { id = stock.Id, locationId = stock.LocationId},
(key, s) => new { key.id, key.locationId, total = s.Sum(ss => ss.Quantity) });

foreach (var result in results)
{
Console.WriteLine(result.id);
Console.WriteLine(result.locationId);
Console.WriteLine(result.total);
}

ответил(а) 2021-01-19T21:04:41+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема