2

I have a generic list ItemList where if each ListID doesn't contain all possible ItemIDs, then I'd like to remove them from the list.

I'll illustrate below with a cut down version of the data:

MaxItemID = 4

ListID       ItemID       

   1            1       
   2            1 
   2            2 
   2            3
   2            4
   3            1
   3            2 
   4            1
   4            2
   4            3
   4            4
   5            1
   5            2

And the data I'd like out of that would be:

ListID       ItemID       

   2            1 
   2            2 
   2            3
   2            4
   4            1
   4            2
   4            3
   4            4

I asked a similar question yesterday relating just to SQL, and got an answer of using Exists, which was good, but I've since discovered that the maximum number of items could vary, and also I think I'd prefer to use linq. I thought using a distinct().Count() technique could work, but had no success so far, and it sounds like it's not the most efficient way either.

Thanks very much

Amar Palsapure
  • 9,590
  • 1
  • 27
  • 46
e-on
  • 1,547
  • 8
  • 32
  • 65

4 Answers4

2

I hope this is your purpose:

var list = 
     ItemList
    .GroupBy(p=>p.ListID)
    .Where(p=>p.Distinct().Count()<MaxItemID)
    .ToArray()
Reza ArabQaeni
  • 4,848
  • 27
  • 46
2
int distinctIDs = items.Select(x => x.ItemID).Distinct().Count();
var newList = items
    .GroupBy(x => x.ListID)
    .Where( x => x.Select(y=>y.ItemID).Distinct().Count() ==  distinctIDs)
    .SelectMany(x => x.ToList())
    .ToList();
L.B
  • 114,136
  • 19
  • 178
  • 224
  • This is excellent L.B. - works a treat. Thank you. Thanks also to Reza - your answer worked as well, but this is the one I implemented. – e-on Feb 02 '12 at 17:31
2

Try this

var query = from baseItem in itemList
    group baseItem by baseItem.ListID into gr
    where gr.Count() == MaxItemID
    join selectItem in itemList on gr.Key equals selectItem.ListID
    select selectItem;

var requiredList = query.ToList();

This works, check the screen shot.enter image description here

EDIT

If duplicates are allowed then query can be modified as

var query = from item in itemList
    group item by item.ListID into gr
    where gr.Select(s=>s.ItemId).Distinct().Count() == MaxItemID
    join i in itemList on gr.Key equals i.ListID
    select i;
Amar Palsapure
  • 9,590
  • 1
  • 27
  • 46
  • Doesn't work if you replace `new Item{ ListID=2 ,ItemID=2}` with `new Item{ ListID=2 ,ItemID=3}` – L.B Feb 02 '12 at 13:09
  • @L.B: If that's the case I have updated the query. OP didn't mention anything about duplicates. But anyways good point, covered it too. – Amar Palsapure Feb 02 '12 at 13:40
0

Cannot comment on the performance of the Linq version but here are Linq and non-linq solutions

hth,
Alan

Edit

The sample data does show any duplicates of listId/ItemId so I omitted the distinct check but if needed it goes as the first part of the linq statement.

Edit 2

Have updated the Linq code.
Haven't updated the non-linq code. Just check to see if same a previous before adding (assumes sorted, ListId, ItemId as per sample input.

[TestMethod]
public void LinqVersion() {

    Dictionary<int, List<ListItem>> found = new Dictionary<int, List<ListItem>>();

    var comparer = new ListItemEqualityComparer();

    var actual = Input.Distinct(comparer).GroupBy(lstItem => lstItem.ListId).Where(grp => grp.Count() == 4).SelectMany(grp => grp);

    Assert.IsTrue(Expected.SequenceEqual(actual, comparer));

}


[TestMethod]
public void NoLinqVersion() {

    Dictionary<int, List<ListItem>> found = new Dictionary<int, List<ListItem>>();

    foreach (var listItem in Input) {
      AddItem(found, listItem.ListId, listItem);
     }

    var actual = new List<ListItem>();
    foreach (var pair in found) {
       if (pair.Value.Count == 4) {
           actual.AddRange(pair.Value);
       }
    }

    Assert.IsTrue(Expected.SequenceEqual(actual, new ListItemEqualityComparer()));

}

    private static void AddItem(IDictionary<int, List<ListItem>> dictionary, int listId, ListItem listItem) {
        if (!dictionary.ContainsKey(listId)) {
            dictionary.Add(listId, new List<ListItem>());
        }
        dictionary[listId].Add(listItem);
    }

    public class ListItem {
        public int ListId { get; set; }
        public int ItemId { get; set; }
    }

    public class ListItemEqualityComparer : IEqualityComparer<ListItem> {
        public bool Equals(ListItem x, ListItem y) {
            return x.ListId == y.ListId && x.ItemId == y.ItemId;
        }

        public int GetHashCode(ListItem obj) {
            return obj.ListId ^ obj.ItemId;
        }
    }

    public List<ListItem> Input = new List<ListItem>(){
        new ListItem{ ListId = 1 , ItemId = 1},
        new ListItem{ ListId = 2 , ItemId = 1},
        new ListItem{ ListId = 2 , ItemId = 2},
        new ListItem{ ListId = 2 , ItemId = 3},
        new ListItem{ ListId = 2 , ItemId = 4},
        new ListItem{ ListId = 3 , ItemId = 1},
        new ListItem{ ListId = 3 , ItemId = 2},
        new ListItem{ ListId = 4 , ItemId = 1},
        new ListItem{ ListId = 4 , ItemId = 2},
        new ListItem{ ListId = 4 , ItemId = 3},
        new ListItem{ ListId = 4 , ItemId = 4},
        new ListItem{ ListId = 5 , ItemId = 1},
        new ListItem{ ListId = 5 , ItemId = 2},
    };


    public List<ListItem> Expected = new List<ListItem>{
        new ListItem{ ListId = 2 , ItemId = 1},
        new ListItem{ ListId = 2 , ItemId = 2},
        new ListItem{ ListId = 2 , ItemId = 3},
        new ListItem{ ListId = 2 , ItemId = 4},
        new ListItem{ ListId = 4 , ItemId = 1},
        new ListItem{ ListId = 4 , ItemId = 2},
        new ListItem{ ListId = 4 , ItemId = 3},
        new ListItem{ ListId = 4 , ItemId = 4}
    };
AlanT
  • 3,627
  • 20
  • 28
  • Linq version wouldn't work if you replaced line '2 2' with '2 3' – L.B Feb 02 '12 at 13:16
  • @L.B. Wouldn't work for dups? Add a Distinct(comparer) as the first linq clause (mentioned in edit). I've updated the code to match – AlanT Feb 02 '12 at 13:56