288

I have a class list of class

public class LinqTest
{
public int id { get; set; }
public string value { get; set; }
}


List<LinqTest> myList = new List<LinqTest>();
myList.Add(new LinqTest() { id = 1, value = "a" });
myList.Add(new LinqTest() { id = 1, value = "b" });
myList.Add(new LinqTest() { id = 2, value = "c" });

I need to select only the distinct id's from that list. ie, my resultant list should only contain

[{id=1,value="a"},{ id = 2, value = "c" }]

How can I do this with linq?

Edit

Input,

id      value
1        a
1        b
2        c
3        d
3        e

Out put should be,

id      value
1        a
2        c
3        d

ie, if there is a repetition of id, result should take the first occurrence only.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Anoop Joshi P
  • 25,373
  • 8
  • 32
  • 53

4 Answers4

587
myList.GroupBy(test => test.id)
      .Select(grp => grp.First());

Edit: as getting this IEnumerable<> into a List<> seems to be a mystery to many people, you can simply write:

var result = myList.GroupBy(test => test.id)
                   .Select(grp => grp.First())
                   .ToList();

But one is often better off working with the IEnumerable rather than IList as the Linq above is lazily evaluated: it doesn't actually do all of the work until the enumerable is iterated. When you call ToList it actually walks the entire enumerable forcing all of the work to be done up front. (And may take a little while if your enumerable is infinitely long.)

The flipside to this advice is that each time you enumerate such an IEnumerable the work to evaluate it has to be done afresh. So you need to decide for each case whether it is better to work with the lazily evaluated IEnumerable or to realize it into a List, Set, Dictionary or whatnot.

Adrian K
  • 9,880
  • 3
  • 33
  • 59
Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
  • 8
    FYI: This won't work unless you convert it back to a list again. GroupBy().Select(First()) will generate an ienumerable and you'll get a conversion error. Do this: myList.GroupBy(test => test.id) .Select(group => group.First()).ToList(); – maplemale Jun 17 '14 at 17:08
  • 2
    And you have to define newList. Then Set result to this newList. Otherwise grouping does not work. This worked for me. 'List newList = new List(); newList = myList.GroupBy(test => test.id) .Select(group => group.First());' – yunus Oct 02 '14 at 06:38
  • 3
    Hey yunus. Thanks for your little comment. I was able to get my IEnumerable to work by using your code and changing List stuff: IEnumerable rows = from c in dc.EDI_RAW_TCRs where c.Batch_ID == 20830 select c; IEnumerable raws = rows; raws = rows.GroupBy(t => t.LabID) .Select(group => group.First()); – JustJohn May 20 '16 at 00:48
  • 3
    in C# it require .FirstOrDefault() instead of .First() – Sruit A.Suk Feb 16 '17 at 04:44
  • 1
    @Sruit, no it doesn't. – Paul Ruane Feb 16 '17 at 10:16
  • 3
    @Sruit, C# doesn't require this, but you EF may give you some headache if you try to do this, say, in the repository. – FailedUnitTest May 02 '17 at 17:53
141

Using morelinq you can use DistinctBy:

myList.DistinctBy(x => x.id);

Otherwise, you can use a group:

myList.GroupBy(x => x.id)
      .Select(g => g.First());
xmedeko
  • 7,336
  • 6
  • 55
  • 85
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
63

You should override Equals and GetHashCode meaningfully, in this case to compare the ID:

public class LinqTest
{
    public int id { get; set; }
    public string value { get; set; }

    public override bool Equals(object obj)
    {
        LinqTest obj2 = obj as LinqTest;
        if (obj2 == null) return false;
        return id == obj2.id;
    }

    public override int GetHashCode()
    {
        return id;
    }
}

Now you can use Distinct:

List<LinqTest> uniqueIDs = myList.Distinct().ToList();
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
30
myList.GroupBy(i => i.id).Select(group => group.First())
Tim Rogers
  • 21,297
  • 6
  • 52
  • 68