19

I have a class called Item. Item has an identifier property called ItemCode which is a string. I would like to get a list of all non-distinct Items in a list of Items.

Example:

List<Item> itemList = new List<Item>()
{
   new Item("code1", "description1"),
   new Item("code2", "description2"),
   new Item("code2", "description3"),
};

I want a list containing the bottom two entries

If I use

var distinctItems = itemsList.Distinct();

I get the list of distinct items which is great, but I want almost the opposite of that. I could subtract the the distinct list from the original list but that wouldn't contain ALL repeats, just one instance of each.

I've had a play and can't figure out an elegant solution. Any pointers or help would be much appreciated. Thanks!

I have 3.5 so LINQ is available

RichK
  • 11,318
  • 6
  • 35
  • 49

4 Answers4

22

My take:

var distinctItems = 
    from list in itemsList
    group list by list.ItemCode into grouped
    where grouped.Count() > 1
    select grouped;
magnus
  • 633
  • 6
  • 12
  • Thanks magnus (and Thomas) and I wouldn't have thought to use GroupBy – RichK Jan 24 '10 at 15:32
  • I recommend changing the variable name to nonDistinctItems for clarity. Great answer and I used this as a solution though. – feyd Mar 03 '20 at 15:40
20

as an extension method:

public static IEnumerable<T> NonDistinct<T, TKey> (this IEnumerable<T> source, Func<T, TKey> keySelector)
{
   return source.GroupBy(keySelector).Where(g => g.Count() > 1).SelectMany(r => r);
}
increddibelly
  • 1,221
  • 1
  • 15
  • 25
2

You might want to try it with group by operator. The idea would be to group them by the ItemCode and taking the groups with more than one member, something like :

var grouped = from i in itemList
              group i by i.ItemCode into g
              select new { Code = g.Key, Items = g };

var result = from g in grouped 
             where g.Items.Count() > 1;
Tomas Vana
  • 18,317
  • 9
  • 53
  • 64
  • I can't get this to compile. It moans about the 'group by' and the 'into' statements – RichK Jan 24 '10 at 15:31
  • I've forgotten the "i" between group and by :$ Now it's fixed, but it's essentially the same thing as magnus wrote. – Tomas Vana Jan 24 '10 at 16:21
0

I'd suggest writing a custom extension method, something like this:

static class RepeatedExtension
{
    public static IEnumerable<T> Repeated<T>(this IEnumerable<T> source)
    {
        var distinct = new Dictionary<T, int>();
        foreach (var item in source)
        {
            if (!distinct.ContainsKey(item))
                distinct.Add(item, 1);
            else
            {
                if (distinct[item]++ == 1) // only yield items on first repeated occurence
                    yield return item;
            }                    
        }
    }
}

You also need to override Equals() method for your Item class, so that items are correctly compared by their code.

Chriso
  • 289
  • 2
  • 8