dawriter
dawriter

Reputation: 435

Iterating through IList<>

Here's a scenario that is playing out for an for each loop that I'm doing;

Basically I want to remove a record from lstMaster if there is a match in lstChild but the results I'm getting back are inconclusive - I should be removing 800 or so records but I'm only removing 256 or so records.

The outer loop is lstMaster has 1600 records. The inner loop is lstChild has 800 which are not suposed to exist in lstMaster.

So if a match is fouond in lstChild then the record is removed in lstMaster.

Am I missing something in the loop?

for(int i=lstMaster.Count-1; i > 0; i--)
   {
   for(int j=lstChildcare.Count-1; j > 0; j--)
      {
        if( lstChildcare[j].school_license == lstMaster[i].school_license )
        {
            textboxStatus.AppendText(Text = "Removing duplicate row: " + i                 + " School: " + lstMaster[i].school_name + Environment.NewLine); 
                    lstMaster.RemoveAt(i);
                    counter++;

                }

          }

      }

Upvotes: 1

Views: 1303

Answers (3)

Yacoub Massad
Yacoub Massad

Reputation: 27861

Since you are looping in reverse (starting from the last item and ending in the first item), then you should be fine.

The only problem I see is removing an item from lstMaster and then trying to access the same index again in the next loop over lstChildcare.

To fix this, simply break the inner loop once you decide to remove the item from the lstMaster list. Since you removed the item, there is no need to continue the loop.

Reaplce this:

lstMaster.RemoveAt(i);
counter++;

Add this:

lstMaster.RemoveAt(i);
counter++;
break;

If for some reason, you need to detect all matches between the parent and child lists, you can simply delete the item form the parent list after you loop through the child list like this:

for (int i = lstMaster.Count - 1; i > 0; i--)
{
    bool delete = false;

    for (int j = lstChildcare.Count - 1; j > 0; j--)
    {
        if (lstChildcare[j].school_license == lstMaster[i].school_license)
        {
            textboxStatus.AppendText(MediaTypeNames.Text = "Removing duplicate row: " + i + " School: " + lstMaster[i].school_name + Environment.NewLine);
            delete = true;
            counter++;
        }
    }

    if(delete)
        lstMaster.RemoveAt(i);
}

Upvotes: 1

Cubicle.Jockey
Cubicle.Jockey

Reputation: 3328

If your lists are actually the concrete type List<T> for you master and child list you can remove unwanted items with a Predicate(T) match in the RemoveAll method. You can also make your ChildCare object inherit from IEquatable<T> to control how it's considered equal and do something similar to below. I made stuff up since I don't know what's in your actual class other than a license BUT you can get the idea from this example. You can copy and paste it straight into LinqPad and run it to give it a try.

void Main()
{
    var masterChildcare = new List<ChildCare>
    {
     new ChildCare{SchoolLicense= "One"},
     new ChildCare{SchoolLicense= "Two"},
     new ChildCare{SchoolLicense= "Three"},
     new ChildCare{SchoolLicense= "Four"},
     new ChildCare{SchoolLicense= "Five"},
     new ChildCare{SchoolLicense= "Six"},
     new ChildCare{SchoolLicense= "Seven"},
     new ChildCare{SchoolLicense= "Eight"},
     new ChildCare{SchoolLicense= "Nine"},
     new ChildCare{SchoolLicense= "Ten"},
    };
    var childChildcare = new List<ChildCare>
    {
        new ChildCare{SchoolLicense= "Three"},
        new ChildCare{SchoolLicense= "Eight"},
        new ChildCare{SchoolLicense= "Nine"}
    };
    masterChildcare.Dump();

    masterChildcare.RemoveAll(childCare => childChildcare.Contains(childCare));
    masterChildcare.Dump();

}

public class ChildCare : IEquatable<ChildCare>
{
    public string SchoolLicense { get; set;}

    public bool Equals(ChildCare other)
    {
        if (other == null)
        {
            return false;
        }

        if (SchoolLicense == other.SchoolLicense)
        {
            return true;
        }
        return false;
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        ChildCare other = obj as ChildCare;
        if (other == null)
        {
            return false;
        }
        return Equals(other);
    }

    public override int GetHashCode()
    {
        return SchoolLicense.GetHashCode();
    }
}

enter image description here

Upvotes: 2

Sanjay Manohar
Sanjay Manohar

Reputation: 7026

Never remove items from a list directly within a loop.

The size of the list changes!

So the for loop that iterates through the list doesn't actually see every item.

For example, if you remove item 2, then i gets incremented to 3 for the next item. But the old item 3 has now become 2. So this item never gets checked.

Instead, create a new list items_to_be_deleted, while in the loop, adding which items need to be deleted.

Then afterwards, delete those items.

Upvotes: -1

Related Questions