0

I've got a problem with removing duplicates at runtime from my list of object.

I would like to remove duplicates from my list of object and then set counter=counter+1 of base object.

public class MyObject 
{
   MyObject(string name) 
   {
      this.counter = 0;
      this.name = name;
   }
   public string name;
   public int counter;
}

List<MyObject> objects_list = new List<MyObject>();
objects_list.Add(new MyObject("john"));
objects_list.Add(new MyObject("anna"));
objects_list.Add(new MyObject("john"));
foreach (MyObject my_object in objects_list) 
{
    foreach (MyObject my_second_object in objects_list) 
    {
        if (my_object.name == my_second_object.name) 
        {
           my_object.counter = my_object.counter + 1;
           objects_list.remove(my_second_object);
        }
    }
}

It return an error, because objects_list is modified at runtime. How can I get this working?

equnex
  • 3
  • 2
  • do a `for` loop instead of a `foreach` and go backwards. this way when you remove an element you can still move up the list – slow Mar 28 '19 at 13:02
  • If you want to mofify the elemnts in a list while iterating that list, use `for` instead of `foreach`. – MakePeaceGreatAgain Mar 28 '19 at 13:02
  • You are by the way comparing object to object.name, which makes no sense and cannot compile, I guess just a mistype? – Ilya Chernomordik Mar 28 '19 at 13:09
  • I can't just use for, because during delete the iterator will change. Also I can't make two backwards for, because I will get the same error. – equnex Mar 28 '19 at 13:09
  • You can't use foreach either to iterate a collection that you change – Ilya Chernomordik Mar 28 '19 at 13:10
  • I know, that's why I'm asking this question. In some other languages it's possible, that's why I'm confused. – equnex Mar 28 '19 at 13:11
  • you could make a new temporary list, and add the items to this second list while checking that they aren't already in the second list, and then replace the original list with the temporary list – jreese Mar 28 '19 at 13:39

2 Answers2

1

With a help of Linq GroupBy we can combine duplicates in a single group and process it (i.e. return an item which represents all the duplicates):

 List<MyObject> objects_list = ...

 objects_list = objects_list
   .GroupBy(item => item.name)            
   .Select(group => {                            // given a group of duplicates we
      var item = group.First();                  // - take the 1st item
      item.counter = group.Sum(g => g.counter);  // - update its counter
      return item;                               // - and return it instead of group
    })
   .ToList();
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • With a little bit change it works: item.counter = group.Sum(g => g.counter + 1); but as far I'm not from C# and it looks a little bit confusing for me. – equnex Mar 28 '19 at 13:25
  • Actually it should work if you just write `item.counter = group.Count()`. You will get the number of same names in each group (you might want to subtract one if that's what you want) – Ilya Chernomordik Mar 28 '19 at 13:32
0

The other answer seem to be correct, though I think it will do scan of the whole list twice, depending on your requirement this might or might not be good enough. Here is how you can do it in one go:

var dictionary = new Dictionary<string, MyObject>();
foreach(var obj in objects_list) 
{
  if(!dictionary.ContainsKey(obj.name)
  {
    dictionary[obj.name] = obj;
    obj.counter++;
  }
  else
  {
      dictionary[obj.name].counter++;
  } 
}

Then dictionary.Values will contain your collection

Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207