3

This may be obvious but I am having trouble getting my head around it.

I have a list of items eg:

BOB 5
Brian 5
Sam 6
James 7
Emily 8
Sandra 8
Michael 8

These are in a List<MyObject>

I want to filter the list so there is only 1 item per ID, by selecting the first one with a unique ID.

I should end up with

BOB 5
Sam 6
James 7
Emily 8

I am having trouble working out a clean way to do this. Any ideas?

NibblyPig
  • 51,118
  • 72
  • 200
  • 356
  • 1
    Probably the groupby in combination with .First() should work, but I am unable to formulate it :( – Christian Sauer Aug 16 '13 at 12:07
  • How do you know to pick Bob instead of Brian? I mean, you say "first" but is there anything that qualifies as first? Datetime stamp, alphabetical order... – SQLMason Aug 16 '13 at 12:08
  • Have you seen this: http://stackoverflow.com/questions/489258/linq-distinct-on-a-particular-property ? – David Tansey Aug 16 '13 at 12:08
  • In my use case it doesn't matter which is chosen but it just has to be one per ID :) – NibblyPig Aug 16 '13 at 12:10
  • @SLC I have no idea how this could be of any use then - in the wild. – SQLMason Aug 16 '13 at 12:11
  • 1
    maybe what you need is distinct? – Tomer W Aug 16 '13 at 12:11
  • Are numbers already sorted, or at least grouped? – Dialecticus Aug 16 '13 at 12:12
  • It may be a good idea to ask yourself why you want poor Brian, Sandra and Michael discarded. Whenever I am trying to do something like this, it almost always turns out I don't actually want to do this. – Martijn Aug 16 '13 at 12:13
  • An example would be displaying something like `Name: Muhammad` In my use the multiple names are all spelling variations of each other and it doesn't matter which one is selected, eg. the list will have `Muhammed, Muhammad, Muhhammed`. The can drill into it more if required but the goal is just to tell them the name is Muhammed or some variation of it. – NibblyPig Aug 16 '13 at 12:17
  • 3
    Don't call the field 'ID' if it's not unique! – Colonel Panic Aug 16 '13 at 12:19
  • @SLC Make a User table with a unique ID and a FK relationship to this table. You'll never have different names again and you'll know who you're talking about. – SQLMason Aug 16 '13 at 12:35
  • I admire your judging, but the ID is unique to the individual - these are their aliases! So Bob Smith ID 24 (ID is a FK in this list!) has 5 aliases with ID 24, and I want to choose one! :P – NibblyPig Aug 16 '13 at 14:36

5 Answers5

8

Use GroupBy and First method combination:

var results = source.GroupBy(x => x.Id).Select(g => g.First()).ToList();

Or as a syntax-based query:

var results = (from i in source
               group i by i.Id into g
               select g.First()).ToList();
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
1

I believe the proper way is to implement IEquatable

Then using .Distinct()

Here is a link of how to use it. This way you have more control over how the first item is handled.

http://msdn.microsoft.com/en-us/library/bb348436.aspx

Smeegs
  • 9,151
  • 5
  • 42
  • 78
  • 1
    Here's a question about how to override [IEquatable](http://stackoverflow.com/questions/3897672/linq-object-equality-and-how-to-properly-override-it?rq=1) – SQLMason Aug 16 '13 at 12:15
0

You need a better Distinct method, that can do 'distinct by key'.

public static IEnumerable<TSource> Distinct<TSource,TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    var seen = new HashSet<TKey>();
    foreach(var x in source)
    {
        var key = keySelector(x);
        if (seen.Add(key);)
            yield return x;
    }
}
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • On an unrelated note, please don't [approve suggested edits](http://stackoverflow.com/review/suggested-edits/2763311) using backticks for emphasis, but reject or improve them - see e.g. [here](http://meta.gaming.stackexchange.com/q/7437/88) why – Tobias Kienzler Aug 21 '13 at 09:05
0

This should be quite fast:

int c = 0;
var results = source.Where(i =>
{
    if (i.Id > c)
    {
        c = i.Id;
        return true;
    }
    else
        return false;
}).ToList();
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78
0

(Arguably) more efficient than GroupBy/First combo, but it requires special null value for ID (-1), and that all items with same ID to be grouped in the list:

        int currentID = -1;
        var unique = list.Where(x =>
        {
            if (currentID != -1 && currentID == x.ID)
                return false;

            currentID = x.ID;
            return true;
        });
Dialecticus
  • 16,400
  • 7
  • 43
  • 103