запрос linq для возврата отдельных значений полей из списка объектов

89
class obj
{
    int typeId; //10 types  0-9 
    string uniqueString; //this is unique
}

Предположим, есть список из 100 элементов obj, но только 10 уникальных идентификаторов типа.
Можно ли написать запрос LINQ, чтобы вернуть 10 уникальных целых значений из списка объектов?

Патрик
источник
Этот вопрос настолько прост и не подходит для награды.
Мыслитель

Ответы:

155
objList.Select(o=>o.typeId).Distinct()
Арсен Мкртчян
источник
2
Вторая форма, похоже, использует перегрузку Distinct, которой, насколько мне известно, не существует.
Джон Скит,
Упс, второй удален, спасибо @Jon Skeet, это можно сделать с IEqualityComparer
Арсен Мкртчян
3
Как упомянул Джон Скит, это вернет только свойства, указанные в Select.
Peet vd Westhuizen
58

Предполагая, что вам нужен полный объект, но вы хотите иметь дело только с отличимостью typeID, в LINQ нет ничего, что могло бы упростить эту задачу. (Если вам просто нужны typeIDзначения, это просто - спроецируйте это с помощью, Selectа затем используйте обычный Distinctвызов.)

В MoreLINQ есть DistinctByоператор, который вы можете использовать:

var distinct = list.DistinctBy(x => x.typeID);

Однако это работает только для LINQ to Objects.

Вы можете использовать группировку или поиск, это немного раздражает и неэффективно:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());
Джон Скит
источник
Чтобы появился DistinctBy, вам необходимо добавить пространство имен Microsoft.Ajax.Utilities
Шилджо Полсон
1
@Shil: Нет, я писал о DistinctBy в MoreLINQ. Ничего общего с Microsoft.Ajax.Utilities.
Джон Скит,
Теперь я вижу, что в LINQ есть перегрузка Distinct, которая принимает IEqualityComparer в качестве параметра и возвращает список отдельных объектов в зависимости от реализации методов в IEqualityComparer
Dipendu Paul
1
@DipenduPaul: Да, но это все равно означает создание компаратора равенства для данного свойства, что раздражает и затрудняет чтение. Если вы можете взять зависимость MoreLINQ, я думаю, что это чище.
Джон Скит
22

Если вы просто хотите использовать чистый Linq, вы можете использовать groupby:

List<obj> distinct =
  objs.GroupBy(car => car.typeID).Select(g => g.First()).ToList();

Если вы хотите, чтобы во всем приложении использовался метод, аналогичный тому, что делает MoreLinq :

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (!seenKeys.Contains(keySelector(element)))
        {
            seenKeys.Add(keySelector(element));
            yield return element;
        }
    }
}

Используя этот метод для поиска различных значений, используя только свойство Id, вы можете использовать:

var query = objs.DistinctBy(p => p.TypeId);

вы можете использовать несколько свойств:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });
JulioCT
источник
Спасибо за раздел с несколькими свойствами !
Nae
7

Конечно, используйте Enumerable.Distinct.

Учитывая набор obj(например foo), вы бы сделали что-то вроде этого:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();
Пончик
источник
5

Я думаю, это то, что вы ищете:

    var objs= (from c in List_Objects 
orderby c.TypeID  select c).GroupBy(g=>g.TypeID).Select(x=>x.FirstOrDefault());      

Похоже на это возвращение отличного IQueryable с помощью LINQ?

Гейдж
источник
.Firstэто нормально, так как у вас не было бы группы, если бы в ней что-то не было.
mqp 03
1
Чтобы GroupByупростить задачу, вы также можете использовать альтернативную перегрузку - см. Мой ответ.
Джон Скит,
3

Если вы просто хотите использовать Linq, вы можете переопределить методы Equals и GetHashCode .

Класс продукции :

public class Product
{
    public string ProductName { get; set; }
    public int Id { get; set; }


    public override bool Equals(object obj)
    {
        if (!(obj is Product))
        {
            return false;
        }

        var other = (Product)obj;
        return Id == other.Id;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Основной метод:

static void Main(string[] args)
    {

        var products = new List<Product>
        {
            new Product{ ProductName="Product 1",Id = 1},
            new Product{ ProductName="Product 2",Id = 2},
            new Product{ ProductName="Product 4",Id = 5},
            new Product{ ProductName="Product 3",Id = 3},
            new Product{ ProductName="Product 4",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
        };

        var itemsDistinctByProductName = products.Distinct().ToList();

        foreach (var product in itemsDistinctByProductName)
        {
            Console.WriteLine($"Product Id : {product.Id} ProductName : {product.ProductName} ");
        }

        Console.ReadKey();
    }
Реза Дженаби
источник
1

Я хотел привязать определенные данные к раскрывающемуся списку, и они должны быть разными. Я сделал следующее:

List<ClassDetails> classDetails;
List<string> classDetailsData = classDetails.Select(dt => dt.Data).Distinct.ToList();
ddlData.DataSource = classDetailsData;
ddlData.Databind();

Посмотрим, поможет ли это

Чарми Вора
источник