Delete from generic list using ForEach

D

Doug

I have a generic list object with a property called,
"MarkedForDeletion". During the course of my processing, some of the
objects in the list will get this property set to true and so at the
end I want to loop through my list and remove any object that has this
property set to true.

Currently I have code like this to accomplish this:

List<MyObject> details = MyMethodToGetDetails();
MyMethodToMarkDetailsForDeletion();

List<MyObject> filteredDetails = details.GetRange(0, details.Count);
MyOjbect currentDetail = null;

for (int deleteIndex = 0; deleteIndex < details.Count; deleteIndex++)
{
currentDetail = details[deleteIndex];
if (currentDetail.MarkedForDeletion)
{
filteredDetails.Remove(currentDetail);
}
}

I want to look at replace this for loop with the ForEach method of the
generic list object. However, the problem is that the Action delegate
of this method does not allow me to pass anything other than the
specific object within the list that I'm dealing with. So I don't
know how to build a delegate that will allow me to remove the object
from within the overall list.

Does anyone have any ideas on how to do this?
 
J

Jon Skeet [C# MVP]

I have a generic list object with a property called,
"MarkedForDeletion". During the course of my processing, some of the
objects in the list will get this property set to true and so at the
end I want to loop through my list and remove any object that has this
property set to true.

In that case, I suggest you use List<T>.RemoveAll with a predicate.

I didn't quite follow your example - if RemoveAll isn't enough for
your needs, could you write a short but complete example which
demonstrates the problem?

See http://pobox.com/~skeet/csharp/complete.html for what I mean by
that.

Jon
 
D

Doug

In that case, I suggest you use List<T>.RemoveAll with a predicate.

This was exactly what I needed and removes my need for the
"filteredDetails" object. Thank you!
 
D

Doug

I do have a follow up question for you though that has to deal with
the methods that the Generic List object provides that require
delegates but the delegates do not allow you to pass in parameters.
I'll explain first and then try to provide the example like you
requested.

In my object that I put into the generic list, I will have a set of
data like below. I need to loop through this data and identify
duplicates (these have been grouped together because they are at a
detail level and at the header level they've been identified as
"potential" duplicates.) If the Destination, Origin, Mileage and Rate
match for a given LocationRateId and LocationRateSequence to at least
one other given LocationRateId and LocationRateSequence then it's a
duplicate. But if for another LocationRateSequence for that same
LocationRateId, there is no match to the LocationRateId and
LocationRateSequence that was matched earlier, then both rows are not
considered duplicates and both are scrapped (yes, I know this sounds
confusing).

LocationRateId LocationRateSequence
Destination Origin
Mileage Rate
12345
1
678 901
100 5.50
12345
2
901 678
100 5.50
67890
1
123 456
95 6.00
67890
2
456 123
95 6.00
99990
1
123 456
95 6.00
99990
2
456 123
95 6.00
55555
1
678 901
100 5.50
55555
2
901 678
100 5.50
88888
1
678 901
100 5.50
88888
2
888 777
150 9.50

If you were to look at this data, LocationRateId 12345 is a duplicate
of 55555 and LocationRateId 67890 is a duplicate of 99990.
LocationRateId 88888 is not a match, because even though
LocationRateSequence 1 for that LocationRateId matches LocationRateId
12345 for it's LocationRateSequence 1, the two don't match for
LocationRateSequence of 2.

Ok, I would like to use the ForEach method in the GenericList object
to do this comparison. But I can't figure out how, because it takes a
delegate that takes no parameters. In order to do this, I would need
to have the overall Generic List passed into the method as well, to
compare with the one object that I'm looking at.

So, here's what I'm doing now, but I'm assuming this is not the best
option performance wise (it sure seems slow)...

Note, I'm including the code for my object, but am not including the
code for getting it filled and put into a generic list...

public class LocationRateDetail
{

private bool _markedForDeletion = false;
private decimal _legCost = decimal.MinValue;
private int _distance = int.MinValue;
private int _legSequenceNumber = int.MinValue;
private int _locationRatesId = int.MinValue;
private string _destinationLocationId = string.Empty;
private string _originLocationId = string.Empty;

public LocationRateDetail()
{ }

public LocationRateDetail(decimal legCost, int distance, int
legSequenceNumber, int locationRatesId, string destinationLocationId,
string originCustomerLocationId,
string originLocationId)
{
_legCost = legCost;
_distance = distance;
_legSequenceNumber = legSequenceNumber;
_locationRatesId = locationRatesId;
_destinationLocationId = destinationLocationId;
_originLocationId = originLocationId;
}


public bool MarkedForDeletion
{
get
{
return _markedForDeletion;
}
set
{
_markedForDeletion = value;
}
}
public decimal LegCost
{
get
{
return _legCost;
}
}
public int Distance
{
get
{
return _distance;
}
}
public int LegSequenceNumber
{
get
{
return _legSequenceNumber;
}
}
public int LocationRatesId
{
get
{
return _locationRatesId;
}
}
public string DestinationLocationId
{
get
{
return _destinationLocationId;
}
}
public string OriginLocationId
{
get
{
return _originLocationId;
}
}
}

Once my generic list has been filled with data, this is the method I
use to try to filter out not duplicates.

public static List<LocationRateDetail>
FilterOutNonDuplicates(List<LocationRateDetail> details)
{
bool keepChecking = true;
int numberDeleted = 0;
LocationRateDetail currentDetail = null;
string locationRatesToDelete = string.Empty;
while (keepChecking == true)
{
numberDeleted = 0;
for (int index = 0; index < details.Count; index++)
{
currentDetail = details[index];
if (currentDetail.MarkedForDeletion == false)
{
if
(locationRatesToDelete.IndexOf(currentDetail.LocationRatesId + ",") >=
0)
{
currentDetail.MarkedForDeletion = true;
}
else
{
if (CheckForDuplicate(currentDetail, details)
== false)
{
currentDetail.MarkedForDeletion = true;
locationRatesToDelete +=
currentDetail.LocationRatesId + ",";
numberDeleted++;
}
}
}
}

if ((numberDeleted == 0) ||
(details.Find(FindUnmarkedRate) == null))
{
keepChecking = false;
}

}

details.RemoveAll(FindMarkedRate);

return details;
}

private static bool FindUnmarkedRate(LocationRateDetail detail)
{
return !detail.MarkedForDeletion;
}

private static bool FindMarkedRate(LocationRateDetail detail)
{
return detail.MarkedForDeletion;
}

private static bool CheckForDuplicate(LocationRateDetail
currentDetail, List<LocationRateDetail> details)
{
bool foundDuplicate = false;
bool stopChecking = true;
LocationRateDetail detail = null;

for (int index = 0; index < details.Count; index++)
{
detail = details[index];
//Don't check any further if this is the same detail as the
current detail.
stopChecking = detail.Equals(currentDetail);
if ((stopChecking == false) &&
(CompareProperties(currentDetail, detail)))
{
foundDuplicate = true;
break;
}
}

return foundDuplicate;
}

private static bool CompareProperties(LocationRateDetail
currentDetail, LocationRateDetail detailToCheck)
{
bool propertiesMatch = false;

if ((currentDetail.LegSequenceNumber ==
detailToCheck.LegSequenceNumber) &&
(currentDetail.OriginLocationId.Trim() ==
detailToCheck.OriginLocationId.Trim()) &&
(currentDetail.DestinationLocationId.Trim() ==
detailToCheck.DestinationLocationId.Trim()) &&
(currentDetail.Distance == detailToCheck.Distance) &&
(currentDetail.LegCost == detailToCheck.LegCost))
{
propertiesMatch = true;
}

return propertiesMatch;
}
 
P

Peter Duniho

Doug said:
[...]
Ok, I would like to use the ForEach method in the GenericList object
to do this comparison. But I can't figure out how, because it takes a
delegate that takes no parameters. In order to do this, I would need
to have the overall Generic List passed into the method as well, to
compare with the one object that I'm looking at.

A suggestion: your post was _very_ long for what appears to be a simple
question. You will get better answers if you can ask your questions
more concisely. I know this from personal experience, having some
problems being brief myself. :)

Anyway, assuming I do understand your question properly, you may find
this recent thread useful:

http://groups.google.com/group/micr....csharp/browse_thread/thread/d4e2630b27c7ab4b

Pete
 
D

Doug

A suggestion: your post was _very_ long for what appears to be a simple
question. You will get better answers if you can ask your questions
more concisely. I know this from personal experience, having some
problems being brief myself. :)

Hi Peter, sorry about that. In a previous post, Jon had suggested
posting my complete code and I tried to shrink it down as much as I
could because long posts can get confusing, but that was the best I
could come up with.

I think you have the gist of what I was looking for in that post. I
didn't put it into a seperate class, but I did have to create a member
level variable like you did in that class in ordre to do my work. I'm
not a big fan of member level variables (it's easy to lose track of
what they should be set to, are they set, etc, etc) but that's what I
went with.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top