Уникальный список товаров с использованием LINQ - PullRequest
19 голосов
/ 06 июля 2010

Я уже некоторое время пользуюсь 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 / C #.

Еще одно уточнение: я также не могу предоставить параметр «Кухонный прибор» и могу просто захотеть уникальные, например, «Кухонный прибор» и «Гостиная» будут возвращены только один раз в категорию, независимо от того, сколько такого типа есть.

Ответы [ 6 ]

44 голосов
/ 06 июля 2010

Гибкий подход

Используйте 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's answer (опционально, возвращая IEnumerable<tKey> вместо IEnumerable<tSource>), или вы можете просто использовать Distinct в сочетании с Select, чтобы избежать необходимости применения IEqualityComparer<Stock> (см. Ниже).


Обновление

В ответ на ваше разъяснение вот моя рекомендация: два метода, по одному для каждой цели( Принцип единой ответственности ):

// 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();
}
4 голосов
/ 06 июля 2010

Звучит так, как будто это простое предложение 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 голосов
/ 06 июля 2010
var kitchenAppliances = stocks.Where(stock => stock.Type == "Kitchen Appliance");
3 голосов
/ 06 июля 2010
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()
1 голос
/ 27 января 2014

Вы могли бы также сделать что-то в этом духе, в этом случае я беру результаты 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();
1 голос
/ 06 июля 2010

Я, вероятно, не понимаю вопроса, но для меня это звучит как довольно простой 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();

Чего я не понимаю, так это того, что вы используете уникальное и уникальное в этом контексте.Объекты уже уникальны, и мне кажется, что вам нужен базовый запрос «дай мне все кухонные приборы».

...