Я уже два года пользуюсь Linq, и думал, что знаю его неплохо... Но вдруг спотнулся на, казалось бы, очень простой задачке. Может, кто подскажет?
У меня есть словарь, в котором ключ - это строка, а значение является списком строк (на самом деле это могут быть и не строки, суть не меняется). Пример. Имеется список товаров, заказанных каждым клиентом. Фамилия клиента является ключом (это плохая практика, но допустим). Для каждого запоминаем список кодов товаров (SKU).
var itemsByCustomer = new Dictionary<string, List<string>>();
itemsByCustomer.Add("Ivanov", new List<string>() { "Sony123", "Toshiba5454" });
itemsByCustomer.Add("Petrov", new List<string>() { "Kodak_4e34_d", "Toshiba5454", "Dell_456g" });
и так далее.
Задача: вывести список, отсортированный по коду товара. Т.е. сгруппировать наоборот - сначала получить список клиентов, которые заказали каждый конкретный товар. Вот примерно такой:
var сustomersByItem = new Dictionary<string, List<string>>();
сustomersByItem.Add("Toshiba5454", new List<string>() { "Ivanov", "Petrov" });
сustomersByItem.Add("Kodak_4e34_d", new List<string>() { "Petrov" });
и так далее. А потом отсортировать этот словарь по ключу.
Я не смог. В результате сделал без Linq, т.е. в цикле перебираю клиентов, и вручную добавляю записи в новый словарь. Единственное Linq-решение, которое пришло в голову - это опять-таки в цикле пройтись по клиентам и добавить .Union() для каждого из них. Но клиентов у меня тысячи, и думаю, что производительность будет ужасная (expression tree разрастется).
Спросил коллег - говорят: "О, это очень сложно". Но ведь с использованием обычного SQL всё бы решилась в два счета! Допустим, у нас были бы таблицы Customer(CustomerCode) и CustomerItem(CustomerCode, SKU). Для моей задачи достаточно было бы написать SELECT * FROM CustomerItem ORDER BY SKU .
Неужели и правда наткнулся на ограничение Linq? Или же, как говорится, проблема в ДНК? :)
Игорь Корхов подсказал ответ. Как я и подозревал, всё просто:
var customersByItem =
from ic in itemsByCustomer
from v in ic.Value
orderby v
select new { Value = v, ic.Key };
7 комментариев:
var customersByItem =
from ic in itemsByCustomer
from v in ic.Value
orderby v
select new { Value = v, ic.Key };
То есть предыдущий запрос выдаст тебе то же, что и "SELECT * FROM CustomerItem ORDER BY SKU".
Это то, что надо? Или тебе надо одним запросом родить сразу Dictionary, а не массив KeyValuePair?
Спасибо, попробую. Нужно перевести в Dictionary, но это я уже и сам умею.
var customersByItem =
from ic in itemsByCustomer
from v in ic.Value
orderby v
select new { Key = v, Value = ic.Key };
var items =
from ic in itemsByCustomer
from v in ic.Value
orderby v
group v by v into g
select g.Key;
var customersByItemDict =
from i in items
select new
{
Key = i,
Value =
(from c in customersByItem
where c.Key == i
select c.Value)
};
var result = new Dictionary>();
foreach (var pair in customersByItemDict)
{
result.Add(pair.Key, pair.Value.ToList());
}
Как-то так. Не очень изящно, цикл в данном случае, по мне, был бы понятней.
Думаю, можно сделать через ToDictionary(), одним махом.
ставь SQL-server и не забивай себе голову всякими циклами :))))))))
Да уже стоит. Но слишком много чего происходит в реальном времени: запросили один веб-сервис, другой, сравнили, записали результат в кэш и пр. Если всё сначала сохранять в базу, то она будет узким местом.
Отправить комментарий