запрос 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 });

Спасибо за раздел с несколькими свойствами !
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();

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.