247

In C#, say I have a class called Note with three string member variables.

public class Note
{
    public string Title;
    public string Author;
    public string Text;
}

And I have a list of type Note:

List<Note> Notes = new List<Note>();

What would be the cleanest way to get a list of all distinct values in the Author column?

I could iterate through the list and add all values that aren't duplicates to another list of strings, but this seems dirty and inefficient. I have a feeling there's some magical Linq construction that'll do this in one line, but I haven't been able to come up with anything.

alamoot
  • 1,966
  • 7
  • 30
  • 50
Darrel Hoffman
  • 4,436
  • 6
  • 29
  • 41

6 Answers6

457
Notes.Select(x => x.Author).Distinct();

This will return a sequence (IEnumerable<string>) of Author values -- one per unique value.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • 3
    Notes.Select(x => x.Author).AsParallel().Distinct(); "AsParallel()" might give some performance benefit, if we doesn't care about order and have more items in the list. – Sai Oct 22 '15 at 20:58
  • 1
    @Kiquenet, distinct considering the `Default` equality comparer. https://msdn.microsoft.com/en-us/library/bb348436(v=vs.110).aspx – Georg Patscheider Jan 21 '16 at 23:01
  • Do we need to add ToList() before .Distinct() ? – Phan Đức Bình Jan 05 '17 at 01:44
  • 5
    Not before the Distinct() but after, if you are trying to convert to a list. Ex: Notes.Select(x => x.Author).Distinct().ToList(); – MTwiford Apr 04 '17 at 20:03
  • @Kirk does the IEnumerable of string still called "Author" or it has changed to anonymous? how to name a column after one has applied distinct to it? thanks – Abubakar Riaz Nov 12 '20 at 05:17
  • @AbubakarRiaz after the application of `.Select`, the new sequence is an enumerable of strings. So there is no meaning to the concept of "column" at that point. It's essentially an array of strings (technicaly an enumerable of strings). – Kirk Woll Nov 12 '20 at 15:12
  • @KirkWoll by column I meant to say "property name" "Author", how to name the array of strings a named property? – Abubakar Riaz Nov 13 '20 at 17:35
  • @AbubakarRiaz what are you actually trying to accomplish? It's an array of strings, so it wouldn't have any "property names" that have any meaning. How do you intend to consume the result? You _could_ project the result back into an anonymous type with an `Author` property. Alternatively, you might be interested in the `DistinctBy` method that Dan Busha mentions below. – Kirk Woll Nov 16 '20 at 22:37
  • @KirkWoll how to "project the result back into an anonymous type with an Author property"? Dan Busha answer needs me to add a library; not looking forward to adding any library. sorry i am not used to this linq & fluent API syntax. kindly help thank you. – Abubakar Riaz Nov 24 '20 at 16:31
  • @KirkWoll I was trying to achieve this https://stackoverflow.com/questions/64798230/distinct-column-in-selectlist-for-dropdown-list. but I have always struggled to. however this is now answered and it was so simple. Thanks alot for pointing out "anonymous type adding" trick. – Abubakar Riaz Nov 24 '20 at 16:38
127

Distinct the Note class by Author

var DistinctItems = Notes.GroupBy(x => x.Author).Select(y => y.First());

foreach(var item in DistinctItems)
{
    //Add to other List
}
Mohammad Atiour Islam
  • 5,380
  • 3
  • 43
  • 48
  • 1
    Isn't it supposed to be `Notes` as in `Notes.GroupBy()` with a plural `s` as `Note` will only hold one single Note? – kkuilla Nov 26 '19 at 16:47
46

Jon Skeet has written a library called morelinq which has a DistinctBy() operator. See here for the implementation. Your code would look like

IEnumerable<Note> distinctNotes = Notes.DistinctBy(note => note.Author);

Update: After re-reading your question, Kirk has the correct answer if you're just looking for a distinct set of Authors.

Added sample, several fields in DistinctBy:

res = res.DistinctBy(i => i.Name).DistinctBy(i => i.ProductId).ToList();
Chad Levy
  • 10,032
  • 7
  • 41
  • 69
Dan Busha
  • 3,723
  • 28
  • 36
  • Excellent, thank you. I love that it keeps the correct order if I order the list before I call the Distinct method. – Vilhelm Oct 05 '17 at 15:52
  • I updated the links. MoreLINQ is now on Github and can be installed via Nuget: `Install-Package morelinq` – Chad Levy Feb 27 '18 at 18:17
3
public class KeyNote
{
    public long KeyNoteId { get; set; }
    public long CourseId { get; set; }
    public string CourseName { get; set; }
    public string Note { get; set; }
    public DateTime CreatedDate { get; set; }
}

public List<KeyNote> KeyNotes { get; set; }
public List<RefCourse> GetCourses { get; set; }    

List<RefCourse> courses = KeyNotes.Select(x => new RefCourse { CourseId = x.CourseId, Name = x.CourseName }).Distinct().ToList();

By using the above logic, we can get the unique Courses.

Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
Bhaskar
  • 49
  • 2
  • your Distinct before ToList save my time.. I am doing after ToList and gives the error that cannot convert list to Ienumerable. – Ajay2707 Jul 29 '19 at 13:27
  • 3
    Distinct won't work here because for this operator every object is unique based on the hashcode of the object. You need some similar to this - https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/DistinctBy.cs – Bose_geek Jun 28 '20 at 10:25
-1

@if (dataModal.Count > 0)
{
    var DistinctItems = dataModal.GroupBy(x => x.Year).Select(y => y.First());

    <div class="col-md-3">
        <label class="fs-7 form-label text-muted">@Localizer["Year"]</label>
            <InputSelect id="ddlYears" class="form-select" @bind-Value="filter.Year">                            
                <option value="">@Localizer["Select"]</option>
                @foreach (var year in DistinctItems)
                {
                   <option value="@year.Year">
                       @year.Year
                   </option>
                }
            </InputSelect>
    </div>
}
Usman Jalil
  • 121
  • 4
  • 10
  • How is this related to the question? Please always add an explanation, not only code. – Gert Arnold Nov 24 '22 at 13:36
  • If it's only about the second line of code, this merely repeats an [existing answer](https://stackoverflow.com/a/24360130/861716), but using your own objects, which isn't really helpful. – Gert Arnold Nov 24 '22 at 13:58
-3
mcilist = (from mci in mcilist select mci).Distinct().ToList();
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
  • What exactly is mci? Just an example table? The answer is a bit awkward as is. – Brad Koch Oct 09 '13 at 21:47
  • 1
    Yeah, I don't think this does what my original question was looking for (never mind it was a year and a half ago and the project is long since finished). From what I can tell, this would work if I already had a list of Author strings which might contain duplicates, but could not be used to extract such a list from a list of objects which have the Author string as one of their fields. – Darrel Hoffman Oct 10 '13 at 04:41