запит linq для повернення різних значень полів зі списку об’єктів


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

Припустимо, є список із 100 елементами obj, але лише 10 унікальними ідентифікаторами типів.
Чи можна написати запит LINQ, щоб повернути 10 унікальних ints зі списку objs?


Це питання настільки просто і не підходить для щедрості.
Мислитель

Відповіді:


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
Shiljo Paulson

1
@Shil: Ні, я писав про DistinctBy у MoreLINQ. Нічого спільного з Microsoft.Ajax.Utilities.
Джон Скіт,

Тепер я бачу, що в LINQ є перевантаження Distinct, яке приймає IEqualityComparer як параметр і повертає список різних об’єктів залежно від реалізації методів у IEqualityComparer
Dipendu Paul,

1
@DipenduPaul: Так, але це все одно означає створення порівняння рівності для даної властивості, що дратує і ускладнює читання. Якщо ви можете прийняти залежність MoreLINQ, я думаю, що це чистіше.
Jon Skeet,

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

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.