27

How do I select the unique elements from the list {0, 1, 2, 2, 2, 3, 4, 4, 5} so that I get {0, 1, 3, 5}, effectively removing all instances of the repeated elements {2, 4}?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Ozgur Ozcitak
  • 10,409
  • 8
  • 46
  • 56
  • At least outside of C# (I can't say for C# itself), the starting point isn't really a set if it contains duplicates. It might be a multi-set, or a list, or ... – Jonathan Leffler Nov 15 '08 at 16:24

10 Answers10

38
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };

var uniqueNumbers =
    from n in numbers
    group n by n into nGroup
    where nGroup.Count() == 1
    select nGroup.Key;

// { 0, 1, 3, 5 }
Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
  • 4
    OMG. That's fancy. How about HashSet r = new HashSet(numbers); – tymtam Feb 20 '12 at 03:20
  • 2
    @Tymek: The OP wants to remove duplicates, leaving only those numbers which are unique in the original sequence. – Bryan Watts Feb 20 '12 at 04:10
  • I'm lost. The code you provided doesn't modify the original sequence called 'numbers'. – tymtam Mar 01 '12 at 01:36
  • 1
    @Tymek: The OP asked to select the unique numbers, which implies creating a new sequence by examining the existing sequence. – Bryan Watts Mar 01 '12 at 03:11
  • In this case I don't understand your first comment. That's what HashSet would do if you create it from the 'numbers' variable you shown. It would contain only the unique values: 0,1,2,3,4,5. :/ – tymtam Mar 01 '12 at 05:47
  • Ha! I understood it now! {0,1,2,2,2,3,4,4,5} -> {0, 3}, not {0,1,2,3,4,5} – tymtam Mar 01 '12 at 05:56
  • 3
    @Tymek: Very close. It would be {0, 1, 3, 5}, since only 2 and 4 repeat. But I think you get the idea. – Bryan Watts Mar 01 '12 at 05:57
20
var nums = new int{ 0...4,4,5};
var distinct = nums.Distinct();

make sure you're using Linq and .NET framework 3.5.

CVertex
  • 17,997
  • 28
  • 94
  • 124
14

With lambda..

var all = new[] {0,1,1,2,3,4,4,4,5,6,7,8,8}.ToList();
var unique = all.GroupBy(i => i).Where(i => i.Count() == 1).Select(i=>i.Key);
Barbaros Alp
  • 6,405
  • 8
  • 47
  • 61
  • Note that in order to use this solution with objects, where you want to get a list objects with a specific key/ field that's unique, you'll need to replace that final Select with a SelectMany: `.SelectMany(i => i)`, which will flatten the `IEnumerable>` back down to an `IEnumerable`. – zcoop98 Mar 24 '21 at 00:22
  • Yet another way to do this with objects or otherwise is to replace the Where and Select altogether with `.Select(x => x.FirstOrDefault())` (Gleaned from [this blog post](https://vmsdurano.com/various-ways-to-get-distinct-values-from-a-listt-using-linq/)). – zcoop98 Mar 24 '21 at 00:28
10

C# 2.0 solution:

static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
    Dictionary<T, int> counts = new Dictionary<T, int>();

    foreach (T item in things)
    {
        int count;
        if (counts.TryGetValue(item, out count))
            counts[item] = ++count;
        else
            counts.Add(item, 1);
    }

    foreach (KeyValuePair<T, int> kvp in counts)
    {
        if (kvp.Value == 1)
            yield return kvp.Key;
    }
}
Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Matt Howells
  • 40,310
  • 20
  • 83
  • 102
9

Here is another way that works if you have complex type objects in your List and want to get the unique values of a property:

var uniqueValues= myItems.Select(k => k.MyProperty)
                  .GroupBy(g => g)
                  .Where(c => c.Count() == 1)
                  .Select(k => k.Key)
                  .ToList();

Or to get distinct values:

var distinctValues = myItems.Select(p => p.MyProperty)
                            .Distinct()
                            .ToList();

If your property is also a complex type you can create a custom comparer for the Distinct(), such as Distinct(OrderComparer), where OrderComparer could look like:

public class OrderComparer : IEqualityComparer<Order>
{
    public bool Equals(Order o1, Order o2)
    {
        return o1.OrderID == o2.OrderID;
    }

    public int GetHashCode(Order obj)
    {
        return obj.OrderID.GetHashCode();
    }
}
Atomic Star
  • 5,427
  • 4
  • 39
  • 48
  • 1
    i liked this better. shorter and easier to read (subjective?) with the lamba expressions. – Bahamut Jul 26 '12 at 08:41
  • @EwaldStieger, he doesn't want to leave a single instance for every value, he wants to REMOVE ALL THE INSTANCES of the values that are present more than 1 time. So how the `Distinct()` can achieve this? (I'm surprised of the 8 upvotes since your answer doesn't resolve the issue correctly.) – Massimiliano Kraus Oct 26 '16 at 18:07
  • @MassimilianoKraus Yes you are correct. I missed that in my original answer and have updated it now – Atomic Star Oct 28 '16 at 06:57
3

If Linq isn't available to you because you have to support legacy code that can't be upgraded, then declare a Dictionary, where the first int is the number and the second int is the number of occurences. Loop through your List, loading up your Dictionary. When you're done, loop through your Dictionary selecting only those elements where the number of occurences is 1.

Corey Trager
  • 22,649
  • 18
  • 83
  • 121
2

I believe Matt meant to say:

 static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
 {
     Dictionary<T, bool> uniques = new Dictionary<T, bool>();
     foreach (T item in things)
     {
         if (!(uniques.ContainsKey(item)))
         {
             uniques.Add(item, true);
         }
     }
     return uniques.Keys;
 }
Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
2

There are many ways to skin a cat, but HashSet seems made for the task here.

var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };

HashSet<int> r = new HashSet<int>(numbers);

foreach( int i in r ) {
    Console.Write( "{0} ", i );
}

The output:

0 1 2 3 4 5

tymtam
  • 31,798
  • 8
  • 86
  • 126
0

Here's a solution with no LINQ:

var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };

// This assumes the numbers are sorted
var noRepeats = new List<int>();
int temp = numbers[0]; // Or .First() if using IEnumerable
var count = 1;
for(int i = 1; i < numbers.Length; i++) // Or foreach (var n in numbers.Skip(1)) if using IEnumerable
{
    if (numbers[i] == temp) count++;
    else
    {
        if(count == 1) noRepeats.Add(temp);
        temp = numbers[i];
        count = 1;
    }
}
if(count == 1) noRepeats.Add(temp);

Console.WriteLine($"[{string.Join(separator: ",", values: numbers)}] -> [{string.Join(separator: ",", values: noRepeats)}]");

This prints:

[0,1,2,2,2,3,4,4,5] -> [0,1,3,5]
tymtam
  • 31,798
  • 8
  • 86
  • 126
-1

In .Net 2.0 I`m pretty sure about this solution:

public IEnumerable<T> Distinct<T>(IEnumerable<T> source)
{
     List<T> uniques = new List<T>();
     foreach (T item in source)
     {
         if (!uniques.Contains(item)) uniques.Add(item);
     }
     return uniques;
}