Уникальный список элементов, использующих LINQ
Я использую LINQ некоторое время, но, похоже, что-то застрял в отношении уникальных элементов, у меня есть список:
List<Stock> stock = new List<Stock>();
У этого есть следующие Свойства: ИД строки, строка Тип, строка Описание, пример:
public class Stock
{
public string ID { get; set; }
public string Type { get; set; }
public string Description { get; set; }
}
Я хочу иметь запрос LINQ, который будет группировать элементы в запасе по их типу и возвращать это как новый список запасов (он должен быть того же типа, что и исходный список запасов).
Пример данных:
ID Type Description
----------------------------------------------
1 Kitchen Appliance Dishwasher
2 Living Room Television
3 Kitchen Appliance Washing Machine
4 Kitchen Appliance Fridge
...
Мой запрос Linq хочет вернуть все кухонные приборы для примера.
Поэтому я передал бы это как "тип" в запрос, и он вернул бы пункты 1, 3 и 4
из этого списка примеров.
Этот возвращаемый список также должен иметь тип: List<Stock>
.
По сути, я хочу список уникальных элементов по типу, вроде SQL Distinct, как это сделать в LINQ?
Альтернативные решения прекрасны, но должны быть только кодом клиента Silverlight/С#.
Еще одно уточнение заключается в том, что я также не могу предоставить параметр "Kitchen Appliance" и может просто хотеть уникальные, например, он будет возвращать кухонную технику и гостиную каждый раз, когда они будут выглядеть как категория, независимо от того, сколько этого типа есть.
Ответы
Ответ 1
Гибкий подход
Используйте GroupBy
и ToDictionary
, чтобы создать словарь значений List<Stock>
, введенных в свойству Type
:
var appliancesByType = stock
.GroupBy(item => item.Type)
.ToDictionary(grp => grp.Key, grp => grp.ToList());
Тогда вы можете легко получить доступ к самим типам, а также к списку для любого заданного типа:
// List of unique type names only
List<string> stockTypes = appliancesByType.Keys.ToList();
// Or: list of one stock item per type
List<Stock> exampleStocks = appliancesByType
.Select(kvp => kvp.Value[0])
.ToList();
// List of stock items for a given type
List<Stock> kitchenAppliances = appliancesByType["Kitchen Appliance"];
Этот подход действительно заботится обо всех ваших потребностях, как я вижу. Но для некоторых других вариантов см. Ниже.
Альтернативный (быстрый и грязный) подход
Вы всегда можете просто использовать Where
, чтобы получить элементы типа, который вы хотите, а затем ToList
, чтобы поместить эти элементы в новый List<Stock>
:
List<Stock> kitchenAppliances = stock
.Where(item => item.Type == "Kitchen Appliance")
.ToList();
В ответ на эту последнюю часть:
Еще одно уточнение заключается в том, что я также может не указывать параметр "Кухонная техника" и может просто хотеть уникальные, например возврат кухонной техники и жилья Комната раз в один независимо от того, сколько из них Тип есть.
Здесь вам кажется, что вы хотите что-то совершенно другое: в основном поведение Distinct
. Для этой функциональности вы могли бы по существу пойти с ответами в формате Soonts (необязательно, возвращая IEnumerable<tKey>
вместо IEnumerable<tSource>
), или вы могли бы просто использовать Distinct
в комбинации с Select
, чтобы избежать необходимости реализации IEqualityComparer<Stock>
(см. ниже).
Update
В ответ на ваше разъяснение, здесь моя рекомендация: два метода, по одному для каждой цели (принцип единой ответственности):
// This will return a list of all Stock objects having the specified Type
static List<Stock> GetItemsForType(string type)
{
return stock
.Where(item => item.Type == type)
.ToList();
}
// This will return a list of the names of all Type values (no duplicates)
static List<string> GetStockTypes()
{
return stock
.Select(item => item.Type)
.Distinct()
.ToList();
}
Ответ 2
Похоже, что это простое предложение Where.
List<Stock> kitchen= stock.Where(s=>s.Type=="Kitchen Appliance")
.OrderBy(s=>s.Description).ToList();
Если вы хотите строго Types
, содержащиеся в исходном списке:
string[] typesFound = stock.Select(s=>s.Type).Distinct().ToArray();
Ответ 3
static class EnumerableEx
{
// Selectively skip some elements from the input sequence based on their key uniqueness.
// If several elements share the same key value, skip all but the 1-st one.
public static IEnumerable<tSource> uniqueBy<tSource, tKey>( this IEnumerable<tSource> src, Func<tSource, tKey> keySelecta )
{
HashSet<tKey> res = new HashSet<tKey>();
foreach( tSource e in src )
{
tKey k = keySelecta( e );
if( res.Contains( k ) )
continue;
res.Add( k );
yield return e;
}
}
}
// Then later in the code
List<Stock> res = src.uniqueBy( elt => elt.Type ).ToList()
Ответ 4
var kitchenAppliances = stocks.Where(stock => stock.Type == "Kitchen Appliance");
Ответ 5
Я, вероятно, не понимаю вопроса, но это звучит для меня как довольно простой Linq-запрос:
List<Stock> stock = new List<Stock>();
... populate your list as per your example
List<Stock> kitchenAppliances =
(from obj in stock
where obj.Type == "Kitchen Appliance"
select obj).ToList();
или если вы предпочитаете синтаксис метода расширения:
List<Stock> kitchenAppliances =
stock.Where(obj => obj.Type == "Kitchen Appliance").ToList();
То, что я не понимаю, - это использование вами уникального и уникального в этом контексте. Объекты уже уникальны, и мне кажется, что вам нужен базовый запрос "дайте мне все кухонные приборы".
Ответ 6
Вы также можете сделать что-то в этом направлении, в этом случае я беру результаты lucene, а затем вытаскиваю узлы umbraco в анонимный тип
var datasource = (from n in SearchResults
select new
{
PageLink = uQuery.GetNode(n.Id).NiceUrl,
uQuery.GetNode(n.Id).Name,
Date = uQuery.GetNode(n.Id).GetProperty("startDate"),
IntroText = string.IsNullOrEmpty(uQuery.GetNode(n.Id).GetProperty("introText").Value) ? string.Empty : uQuery.GetNode(n.Id).GetProperty("introText").Value,
Image = ImageCheck(uQuery.GetNode(n.Id).GetProperty("smallImage").Value)
}).Distinct().ToList();