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

Assume there is a list with 100 elements of objs, but only 10 unique typeIDs.

Is it possible to write a LINQ query that returns the 10 unique ints from the list of objs?

Andreas
  • 5,393
  • 9
  • 44
  • 53
patrick
  • 16,091
  • 29
  • 100
  • 164

7 Answers7

212
objList.Select(o=>o.typeId).Distinct()
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
78

Assuming you want the full object, but only want to deal with distinctness by typeID, there's nothing built into LINQ to make this easy. (If you just want the typeID values, it's easy - project to that with Select and then use the normal Distinct call.)

In MoreLINQ we have the DistinctBy operator which you could use:

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

This only works for LINQ to Objects though.

You can use a grouping or a lookup, it's just somewhat annoying and inefficient:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());
Malice
  • 3,927
  • 1
  • 36
  • 52
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • To have DistinctBy to appear you need to add the namespace Microsoft.Ajax.Utilities – Shiljo Paulson Aug 11 '16 at 01:33
  • 2
    @Shil: No, I was writing about the DistinctBy in MoreLINQ. Nothing to do with Microsoft.Ajax.Utilities. – Jon Skeet Aug 11 '16 at 01:38
  • Now I can see there is an overload of Distinct in LINQ which take an IEqualityComparer as parameter and return a list of distinct objects depending upon the implementation of methods in IEqualityComparer – Dipendu Paul Oct 11 '19 at 06:24
  • 2
    @DipenduPaul: Yes, but that still means creating an equality comparer for a given property, which is annoying and makes it harder to read. If you can take the MoreLINQ dependency, I think that's cleaner. – Jon Skeet Oct 11 '19 at 06:41
54

If just want to user pure Linq, you can use groupby:

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

If you want a method to be used all across the app, similar to what MoreLinq does:

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;
        }
    }
}

Using this method to find the distinct values using just the Id property, you could use:

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

you can use multiple properties:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });
KyleMit
  • 30,350
  • 66
  • 462
  • 664
JulioCT
  • 1,057
  • 8
  • 7
  • 1
    Thanks for the _multiple properties_ part! – Nae Apr 22 '19 at 12:23
  • 3
    .NET 6 now has the distinctBy extension method. [Documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinctby?view=net-6.0) – Isaac Ikusika Oct 17 '22 at 04:02
7

Sure, use Enumerable.Distinct.

Given a collection of obj (e.g. foo), you'd do something like this:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();
Donut
  • 110,061
  • 20
  • 134
  • 146
5

I think this is what your looking for:

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

Similar to this Returning a Distinct IQueryable with LINQ?

Community
  • 1
  • 1
Gage
  • 7,365
  • 9
  • 47
  • 77
  • 1
    `.First` is fine, since you wouldn't have the group if there wasn't something in it. – mqp Jun 03 '11 at 18:54
  • 2
    You can use an alternative overload of `GroupBy` to make this simpler too - see my answer for an example. – Jon Skeet Jun 03 '11 at 18:55
4

If just want to use Linq, you can override Equals and GetHashCode methods.

Product class:

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();
    }
}

Main Method:

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();
    }
Reza Jenabi
  • 3,884
  • 1
  • 29
  • 34
  • This only makes sense for a class where equality is completely determined by the property you wish to query! I recommend using any other answer instead. – ToolmakerSteve Oct 10 '21 at 21:20
3

I wanted to bind a particular data to dropdown and it should be distinct. I did the following:

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

See if it helps

joergl
  • 2,850
  • 4
  • 36
  • 42