Okay, so I wasn't completely sure what headline would fit my problem, but here goes the description:
I have objects than can reference other objects, to create dropdown lists where the content/values is dependant on what values is chosen in "parent" dropdowns.
My dropdown objects contain an id, and a parentId (and other stuff, not relevant here).
I want to prevent the users from making infinite loops, like this:
List 1 (Dependant on list 3)
List 2 (Dependant on list 1)
List 3 (Dependant on list 2)
I've tried writing a recursive method to prevent it, but I cannot figure out the logic.
Could anyone tell me how you would ensure that an object isn't referencing it self "down the line" ? Or provide an example perhaps.
Any help is much appreciated.
The simplest way I can think of is to create a flattened list. Recursively iterate the objects and store each reference in a list. As you find new objects check each one in the list.
You'll either encounter an object referencing itself or run out of objects to search.
This method being suitable will depend on your requirements, speed / memory/ number of items in the list.
Since all object contain an id the list could store/check that instead if you need to check value equality instead of reference equality
If you have written a recursive function to manage those lists, one solution could be to create a list of elements and pass it as parameter into the recursive function and en each iteration add the current item to the list. To stop the recursive function, only check if the current item has been added previously to the list.
If you iterate through the actual elements of each list by relying on specific counters for each list you shouldn't find any problem. The most likely way to provoke an infinite loop is changing the value of a counter from an external source. Example:
for(int i = 0; i < max_i; i++)
{
if(val1[i] != null)
{
for(int j = 0; j < max_j; j++)
{
if(val2[j] != null)
{
//Delete or anything
//YOU CANNOT AFFECT NEITHER i NOR j DIRECTLY.
}
}
}
If you want to account for varying values of j in the internal part, you should rely on a different variable. Example:
if(val2[j] != null)
{
int j2 = j;
//Do whatever with j2, never with j
}
By doing this (associating different counters to different loops), no endless loop will occur. An endless loop occurs when: i = 1, 2, 3, 4 and suddenly i is changed to 2 by an "external source"; thus solution: NEVER change i other than through the for loop.
Thanks everyone for your input on this. I went with James suggestion using a list and ended up with the following code (which may or may not make sense for anyone else but me)
public static bool BadParent(int fieldId, int childId, List<int> list)
{
if (list == null)
list = new List<int>();
bool returnValue = true;
var field = EkstraFelterBLL.getEkstraFeltUdfraEkstraFeltId(fieldId);
if (field != null)
{
if (field.ParentEkstraFeltId == childId)
returnValue = false; //loop reference, fail
else if (list.Contains(field.EkstraFeltId))
returnValue = false; //already been in the cycle, fail
else
{
list.Add(field.EkstraFeltId);
returnValue = BadParent(field.ParentEkstraFeltId, childId, list);
}
}
return returnValue;
}
Related
Looking for a solution to find an object.id and get all the parent_id's in an array of objects, and then set object.missed = true.
Object.id, and Object parent_id. If the object doesn't have a parent_id, parent_id = id.
I know how to do it for one level of parent_id's. How can I go unlimited levels deep? Below is the code I have for searching the 1 level.
public class EPlan
{
public int id;
public int parent_id;
public bool is_repeatable;
public bool missed;
}
EPlan[] plans = Array.FindAll(eventsPlan, item => item.parent_id == event_id);
foreach (EPlan plan in plans)
{
plan.missed = true;
plan.is_repeatable = false;
}
I'm trying to search for event_id an int. So I search all of the object.id's for event_id. Once I find object.id == event_id. I need to set object.is_repeatable = false and object.missed = true.
Then I need to search all of the objects.parent_id for current object.id (event_id). Change all of those object to the same as above.
Then I need to check all of those object.id's against all of the object.parent_id's and do the same to those. Like a tree affect. 1 event was missed, and any of the events that are parented to that event need to be set as missed as well.
So far, all I can do is get 1 level deep, or code multiple foreach loops in. But it could be 10 or more levels deep. So that doesn't make sense.
Any help is appreciated. There has to be a better way that the multiple loops.
I too was confused by the question, save for the one line you said:
1 event was missed, and any of the events that are parented to that event need to be set as missed as well.
With that in mind, I suggest the following code will do what you're looking for. Each time you call the method, it will find all of the objects in the array that match the ID and set the event as Missed and Is_Repeatable appropriately.
It also keeps a running list of the Parent_ID's it found during this scan. Once the loop is finished it will call itself, using the list of parent id values instead of the passed in list of events ids it just used. That is the trick that makes the recursion work here.
To start the process off, you call the method with the single event ID you did for 1-level search.
findEvents(new List<string>{event_id}, eventsPlan);
private void findEvents(List<int> eventIDs, EPlan[] eventsPlan)
{
foreach (int eventID in eventIDs)
{
EPlan[] plans = Array.FindAll(eventsPlan, item => item.parent_id == eventID);
List<int> parentIDs = new List<int>();
foreach (EPlan plan in plans)
{
plan.missed = true;
plan.is_repeatable = false;
parentIDs.Add(plan.parent_id);
}
if (parentIDs.Count > 0)
findEvents(parentIDs, eventsPlan);
}
}
I also recommend that if you have the chance to reengineer this code to not use arrays, but a Generic Collection (like List<EPlan>) you can avoid the performance penalty this code has because it's building new arrays in memory each time you call the Array.FindAll method. Using the Generic Collection, or even using old-school foreach loop will work faster when processing a lot of data here.
Update 1:
To answer your question about how you might go about this using a Generic Collection instead:
private void findEventsAsList(List<int> eventIDs, List<EPlan> eventsPlans)
{
List<int> parentIDs = new List<int>();
foreach (EPlan plan in eventsPlans.Where(p => eventIDs.Contains(p.parent_id)))
{
plan.missed = true;
plan.is_repeatable = false;
parentIDs.Add(plan.parent_id);
}
findEventsAsList(parentIDs, eventsPlan);
}
For now, the best I could think of is:
bool oneMoreTime = true;
while (oneMoreTime)
{
ItemType toDelete=null;
oneMoreTime=false;
foreach (ItemType item in collection)
{
if (ShouldBeDeleted(item))
{
toDelete=item;
break;
}
}
if (toDelete!=null)
{
collection.Remove(toDelete);
oneMoreTime=true;
}
}
I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.
The "RemoveAll" method is best.
Another common technique is:
var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList();
foreach(var itemToBeDeleted in itemsToBeDeleted)
collection.Remove(itemToBeDeleted);
Another common technique is to use a "for" loop, but make sure you go backwards:
for (int i = collection.Count - 1; i >= 0; --i)
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i);
Another common technique is to add the items that are not being removed to a new collection:
var newCollection = new List<whatever>();
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i))
newCollection.Add(item);
And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:
var newCollection = oldCollection;
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i))
newCollection = newCollection.Remove(item);
or
var newCollection = ImmutableCollection<whatever>.Empty;
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i))
newCollection = newCollection.Add(item);
And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.
Just as I finished typing I remembered that there is lambda-way to do it.
collection.RemoveAll(i=>ShouldBeDeleted(i));
Better way?
A forward variation on the backward for loop:
for (int i = 0; i < collection.Count; )
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i)
else
i++;
You cannot delete from a collection inside a foreach loop (unless it is a very special collection having a special enumerator). The BCL collections will throw exceptions if the collection is modified while it is being enumerated.
You could use a for loop to delete individual elements and adjust the index accordingly. However, doing that can be error prone. Depending on the implementation of the underlying collection it may also be expensive to delete individual elements. For instance deleting the first element of a List<T> will copy all the remaning elements in the list.
The best solution is often to create a new collection based on the old:
var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList();
Use ToList() or ToArray() to create the new collection or initialize your specific collection type from the IEnumerable returned by the Where() clause.
The lambda way is good. You could also use a regular for loop, you can iterate lists that a for loop uses within the loop itself, unlike a foreach loop.
for (int i = collection.Count-1; i >= 0; i--)
{
if(ShouldBeDeleted(collection[i])
collection.RemoveAt(i);
}
I am assuming that collection is an arraylist here, the code might be a bit different if you are using a different data structure.
I have the classic case of trying to remove an item from a collection while enumerating it in a loop:
List<int> myIntCollection = new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);
foreach (int i in myIntCollection)
{
if (i == 42)
myIntCollection.Remove(96); // The error is here.
if (i == 25)
myIntCollection.Remove(42); // The error is here.
}
At the beginning of the iteration after a change takes place, an InvalidOperationException is thrown, because enumerators don’t like when the underlying collection changes.
I need to make changes to the collection while iterating. There are many patterns that can be used to avoid this, but none of them seems to have a good solution:
Do not delete inside this loop, instead keep a separate “Delete List”, that you process after the main loop.
This is normally a good solution, but in my case, I need the item to be gone instantly as “waiting” till after
the main loop to really delete the item changes the logic flow of my code.
Instead of deleting the item, simply set a flag on the item and mark it as inactive. Then add the functionality of pattern 1 to clean up the list.
This would work for all of my needs, but it means that a lot of code will have to change in order to check the inactive flag every time an item is accessed. This is far too much administration for my liking.
Somehow incorporate the ideas of pattern 2 in a class that derives from List<T>. This Superlist will handle the inactive flag, the deletion of objects after the fact and also will not expose items marked as inactive to enumeration consumers. Basically, it just encapsulates all the ideas of pattern 2 (and subsequently pattern 1).
Does a class like this exist? Does anyone have code for this? Or is there a better way?
I’ve been told that accessing myIntCollection.ToArray() instead of myIntCollection will solve the problem and allow me to delete inside the loop.
This seems like a bad design pattern to me, or maybe it’s fine?
Details:
The list will contain many items and I will be removing only some of them.
Inside the loop, I will be doing all sorts of processes, adding, removing etc., so the solution needs to be fairly generic.
The item that I need to delete may not be the current item in the loop. For example, I may be on item 10 of a 30 item loop and need to remove item 6 or item 26. Walking backwards through the array will no longer work because of this. ;o(
The best solution is usually to use the RemoveAll() method:
myList.RemoveAll(x => x.SomeProp == "SomeValue");
Or, if you need certain elements removed:
MyListType[] elems = new[] { elem1, elem2 };
myList.RemoveAll(x => elems.Contains(x));
This assume that your loop is solely intended for removal purposes, of course. If you do need to additional processing, then the best method is usually to use a for or while loop, since then you're not using an enumerator:
for (int i = myList.Count - 1; i >= 0; i--)
{
// Do processing here, then...
if (shouldRemoveCondition)
{
myList.RemoveAt(i);
}
}
Going backwards ensures that you don't skip any elements.
Response to Edit:
If you're going to have seemingly arbitrary elements removed, the easiest method might be to just keep track of the elements you want to remove, and then remove them all at once after. Something like this:
List<int> toRemove = new List<int>();
foreach (var elem in myList)
{
// Do some stuff
// Check for removal
if (needToRemoveAnElement)
{
toRemove.Add(elem);
}
}
// Remove everything here
myList.RemoveAll(x => toRemove.Contains(x));
If you must both enumerate a List<T> and remove from it then I suggest simply using a while loop instead of a foreach
var index = 0;
while (index < myList.Count) {
if (someCondition(myList[index])) {
myList.RemoveAt(index);
} else {
index++;
}
}
I know this post is old, but I thought I'd share what worked for me.
Create a copy of the list for enumerating, and then in the for each loop, you can process on the copied values, and remove/add/whatever with the source list.
private void ProcessAndRemove(IList<Item> list)
{
foreach (var item in list.ToList())
{
if (item.DeterminingFactor > 10)
{
list.Remove(item);
}
}
}
When you need to iterate through a list and might modify it during the loop then you are better off using a for loop:
for (int i = 0; i < myIntCollection.Count; i++)
{
if (myIntCollection[i] == 42)
{
myIntCollection.Remove(i);
i--;
}
}
Of course you must be careful, for example I decrement i whenever an item is removed as otherwise we will skip entries (an alternative is to go backwards though the list).
If you have Linq then you should just use RemoveAll as dlev has suggested.
As you enumerate the list, add the one you want to KEEP to a new list. Afterward, assign the new list to the myIntCollection
List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
List<int> newCollection=new List<int>(myIntCollection.Count);
foreach(int i in myIntCollection)
{
if (i want to delete this)
///
else
newCollection.Add(i);
}
myIntCollection = newCollection;
Let's add you code:
List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);
If you want to change the list while you're in a foreach, you must type .ToList()
foreach(int i in myIntCollection.ToList())
{
if (i == 42)
myIntCollection.Remove(96);
if (i == 25)
myIntCollection.Remove(42);
}
For those it may help, I wrote this Extension method to remove items matching the predicate and return the list of removed items.
public static IList<T> RemoveAllKeepRemoved<T>(this IList<T> source, Predicate<T> predicate)
{
IList<T> removed = new List<T>();
for (int i = source.Count - 1; i >= 0; i--)
{
T item = source[i];
if (predicate(item))
{
removed.Add(item);
source.RemoveAt(i);
}
}
return removed;
}
How about
int[] tmp = new int[myIntCollection.Count ()];
myIntCollection.CopyTo(tmp);
foreach(int i in tmp)
{
myIntCollection.Remove(42); //The error is no longer here.
}
If you're interested in high performance, you can use two lists. The following minimises garbage collection, maximises memory locality and never actually removes an item from a list, which is very inefficient if it's not the last item.
private void RemoveItems()
{
_newList.Clear();
foreach (var item in _list)
{
item.Process();
if (!item.NeedsRemoving())
_newList.Add(item);
}
var swap = _list;
_list = _newList;
_newList = swap;
}
Just figured I'll share my solution to a similar problem where i needed to remove items from a list while processing them.
So basically "foreach" that will remove the item from the list after it has been iterated.
My test:
var list = new List<TempLoopDto>();
list.Add(new TempLoopDto("Test1"));
list.Add(new TempLoopDto("Test2"));
list.Add(new TempLoopDto("Test3"));
list.Add(new TempLoopDto("Test4"));
list.PopForEach((item) =>
{
Console.WriteLine($"Process {item.Name}");
});
Assert.That(list.Count, Is.EqualTo(0));
I solved this with a extension method "PopForEach" that will perform a action and then remove the item from the list.
public static class ListExtensions
{
public static void PopForEach<T>(this List<T> list, Action<T> action)
{
var index = 0;
while (index < list.Count) {
action(list[index]);
list.RemoveAt(index);
}
}
}
Hope this can be helpful to any one.
Currently you are using a list. If you could use a dictionary instead, it would be much easier. I'm making some assumptions that you are really using a class instead of just a list of ints. This would work if you had some form of unique key. In the dictionary, object can be any class you have and int would be any unique key.
Dictionary<int, object> myIntCollection = new Dictionary<int, object>();
myIntCollection.Add(42, "");
myIntCollection.Add(12, "");
myIntCollection.Add(96, "");
myIntCollection.Add(25, "");
foreach (int i in myIntCollection.Keys)
{
//Check to make sure the key wasn't already removed
if (myIntCollection.ContainsKey(i))
{
if (i == 42) //You can test against the key
myIntCollection.Remove(96);
if (myIntCollection[i] == 25) //or you can test against the value
myIntCollection.Remove(42);
}
}
Or you could use
Dictionary<myUniqueClass, bool> myCollection; //Bool is just an empty place holder
The nice thing is you can do anything you want to the underlying dictionary and the key enumerator doesn't care, but it also doesn't update with added or removed entries.
I have a String Collection that is populated with ID's like so -->
12345
23456
34567
and so on. What I need to do is at the user's request, and when certain parameters are met, go through that list, starting at the top, and perform a method() using that ID. If successful I would remove it from the list and move on.
I, embarrassingly, have never worked with a collection before in this manner. Can someone point me in the right direction. Examples all seem to be of the Console.Writeline(""); variety.
My base, ignorant, attempt looks like this -->
var driUps = Settings.Default.DRIUpdates.GetEnumerator();
while (driUps.MoveNext())
{
var wasSuccessfull = PerformDRIUpdate(driUps.Current);
if (wasSuccessfull)
{
driUps.Current.Remove(driUps.Current.IndexOf(driUps.Current));
}
}
The part I am most concerned with is the Remove(); Isn't there a better way to get the Current Index? Any and all Tips, Hints, Criticism, Pointers, etc....welcome. Thanks!
You are quite right to be concerned about the 'remove' during enumeration. How about somethign like this:
int idx = 0;
while (idx < strCol.Count)
{
var wasSuccessful = PerformDRIUpdate(strCol[idx]);
if (wasSuccessful)
strCol.RemoveAt(idx);
else
++idx;
}
As suggested by n8wrl, using RemoveAt solves the issue of trying to remove an item whilst enumerating the collection, but for large collections removing items from the front can cause performance issues as the underlying collection is re-built. Work your way from the end of the collection and remove items from that end:
//Loop backwards, as removing from the beginning
//causes underlying collection to be re-built
int index = (strCol.Count - 1);
while (index >= 0)
{
if (PerformDRIUpdate(strCol[index]))
{
strCol.RemoveAt(index);
}
--index;
}
Iterating an enumerator is best done with the foreach(), it does a GetEnumerator() and creates a similar block under the covers to what you're getting at, the syntax is:
foreach(ObjectType objectInstance in objectInstanceCollection)
{
do something to object instance;
}
for you,
List<DRIUpdate> updatesToRemove = new List<DRIUpdate>();
foreach(DRIUpdate driUpdate in Settings.Default.DRIUpdates)
{
if (PerformDRIUpdate(driUpdate))
{
updatesToRemove.Add(driUpdate);
}
}
foreach(DRIUpdate driUpdate in updatesToRemove)
{
Settings.Default.DRIUpdates.Remove(driUpdate);
}
If driUps is an IEnumerable<T>, try this:
driUps = driUps.Where(elem => !PerformDRIUpdate(elem));
Update:
From the example, it seems this is more appropriate:
Settings.Default.DRIUpdates =
Settings.Default.DRIUpdates.Where(elem => !PerformDRIUpdate(elem));
For a List<T>, it's simpler:
list.RemoveAll(PerformDRIUpdate);
I have a list of rows from a dataset that I need to iterate through.
The problem is that the processing in the iteration may delete one or more rows from the list.
Since the list is being modified, I can't use a foreach() loop.
But since it is possible some of the deletions may occur at elements BEFORE the one I'm processing, I also can't use a for() loop (i.e, if I'm processing element , and that results in the deletion of element and also other elements , I can't think of a way to adjust i to correctly point to the element following the one that I was processing).
How would you tackle this problem? My current thought it is to always process the first element in the list. If it gets deleted, process the new first element. If it doesn't get deleted, the move it to an "alreadyProcessed" list, and process the new first element.
Is there an easier way?
Typically this is done with a reverse loop:
List<string> Items = ...
for(int i = Items.Count - 1; i >= 0; i--)
{
if(Items[i] == "DELETE ME")
{
Items.RemoveAt(i);
}
}
This causes the items to be processed in reverse order, so if you delete an item, it does not affect the position of any items still to be processed.
When modifying a list I'm iterating through, I always find it easiest to build a new list with the items I want to keep, and then use the new list to do whatever it was I was going to do.
It really depends on what you're doing with the data when you're done, I suppose.
for(int i = list.Length -1, i >= 0; i--)
{
// process and delete if you want
}
int i = 0;
while (i < dataSet.Tables[0].Rows.Count) {
if (some_condition) {
dataSet.Tables[0].Rows.RemoveAt(i);
continue;
}
i++;
}
If you can get your data into a linked list, you're golden.