Add elements to a list while iterating over it - c#

I'm trying to add new elements to a list of lists while iterating over it
List<List<String>> sets = new List<List<string>>();
foreach (List<String> list in sets)
{
foreach (String c in X)
{
List<String> newSet = ir_a(list, c, productions);
if (newSet.Count > 0)
{
sets.Add(newSet);
}
}
}
The error I get after a few loops is this:
Collection was modified; enumeration operation may not execute
I know the error is caused by modifying the list, so my question is: What's the best or most fancy way to sort this thing out?
Thanks

You might get away with this in other languages but not C#. They do this to avoid funny runtime behaviour that isn't obvious. I prefer to set up a new list of things you are going to add, populate it, and then insert it after the loop.
public class IntDoubler
{
List<int> ints;
public void DoubleUp()
{
//list to store elements to be added
List<int> inserts = new List<int>();
//foreach int, add one twice as large
foreach (var insert in ints)
{
inserts.Add(insert*2);
}
//attach the new list to the end of the old one
ints.AddRange(inserts);
}
}
Imagine that if you had a foreach loop, and you added an element to it each time, then it would never end!
Hope this helps.

Related

list inside list. how to access items inside of it

I'm having some issues with my programm in c#.
Basically I have a list called mainList with 3 items in it. First two items are integers, but third one is another list containing more items.
mainList[0] = 8;
mainList[1] = 1;
mainList[2] = list;
By using foreach loop I'm able to print all of those items.
foreach (var i in (dynamic)(mainList[2]))
{
Console.WriteLine(i);
}
However I don't know how to access them. The thing is that I can't use indexes, because it is not an array. I would like to do something like this:
foreach (var i in (dynamic)(mainList[2]))
{
// First item is in datetime type, so I would like to change it to int
Console.WriteLine(Convert.ToInt64(i[1]));
}
Is there a way to access items inside list like we do it in arrays with a help of indexes?
Lists support the same index-based access as arrays, so you can use
mainList[n]
to access the nth entry in mainList.
I guess you are looking for something like this, although it is really unclear what you're asking:
foreach(var item in mainList)
{
if(item is System.Collections.IEnumerable)
{
foreach(var obj in ((System.Collections.IEnumerable)item))
{
Console.WriteLine(obj);
}
}
}

Why am I getting ArgumentOutOfRangeException in Lists?

I am using List of Lists in my project. When i run program i get ArgumentOutOfRangeException. But there is no range specified in list.
I declared list like this:
public static List<List<string>> list = new List<List<string>>();
Now i want to add my name in the first "list" which is in the List of lists.
list[0].Add("Hussam"); //Here i get ArgumentOutOfRange Exception.
What should I do now?
But there is no range specified in list
No, there's an index specified (as an argument), and that's what's out of range. Look at your code:
list[0].Add("Hussam");
That's trying to use the first list in list - but is list is empty, there is no first element. The range of valid arguments to the indexer is empty, basically.
So first you want:
list.Add(new List<string>());
Now list[0] will correctly refer to your empty List<string>, so you can add "Hussam" to it.
You want to add an item to the first item in an empty list... That isn't going to work. First, add the list inside the other list:
public static List<List<string>> list = new List<List<string>>();
List<string> innerList = new List<string>();
list.Add(innerList);
innerList.Add("Hussam");
Why are you creating a list of a list? Wouldn't List suffice? What is happening here is the inner list is not being initialized.
list.Add(new List<string>());
list[0].Add("Jimmy");
In this case ocurred an exception because you tried acess an index which not exists, then you must add an inner initial list, which could be done follows:
list.Add(new new List<string>());
Or, if you want add an first name directly:
list.Add(new new List<string>(){"Hussam"});
Ok so first, you have to understand that the "index" only comes after the value has been declared. Lists behave different. They are not like arrays. You get the index in which you want to store the item and when you do that, you use the code array[index] = value;.
But in a List, to give a value to a completely new item, you use the method Add(value).
So here's a reminder: Systems.Collections.Generic.List<> has nothing to do with array[ ]s
You cannot access list[0] as there is no item at index 0. The list is empty.
You need to add a new List like this:
list.Add(new List<string> { "Hussam" });
or, assign a list to index 0 and then add to it as per your posted code:
list.Add(new List<string>());
list[0].Add("Hussam");
If you don't always know if the list will be be empty or not you can use FirstOrDefault (a LINQ method) to check if there is any entry at index 0 and assign one if not, otherwise use the existing inner list:
var innerList = list.FirstOrDefault();
if (innerList == null)
{
innerList = new List<string>();
list.Add(innerList);
}
innerList.Add("Hussam");
The problem is, your nested list hasn't been initialized, with anything.
So, calling the first item of the nested list is correctly telling you there is nothing in it.
To verify:
int superlistCounter = 1;
int sublistCounter = 1;
foreach(var sublist in list)
{
Console.WriteLine("Now in List #" + superlistCounter);
foreach(var item in sublist)
{
Console.WriteLine("List item #" + sublistCounter + ": " + item)
}
}
The output will be:
Now in List #1
It sounds like you're expecting:
Now in List #1
List Item #1: Hussam
To fix this, simply initialize your list!
public static List<List<string>> list = new List<List<string>>();
// ...
List<string> subList1 = new List<string>();
list.Add(subList1);
subList1.Add("Hussam");

Parallel loop in c#, accessing the same variable

I have an Item object with a property called generator_list (hashset of strings). I have 8000 objects, and for each object, I'd like to see how it's generator_list intersects with every other generator_list, and then I'd like to store the intersection number in a List<int>, which will have 8000 elements, logically.
The process takes about 8 minutes, but only a few minutes with parallel processing, but I don't think I'm doing the parallel part right, hence the question. Can anyone please tell me if and how I need to modify my code to take advantage of the parallel loops?
The code for my Item object is:
public class Item
{
public int index { get; set; }
public HashSet<string> generator_list = new HashSet<string>();
}
I stored all my Item objects in a List<Item> items (8000 elements). I created a method that takes in items (the list I want to compare) and 1 Item (what I want to compare to), and it's like this:
public void Relatedness2(List<Item> compare, Item compare_to)
{
int compare_to_length = compare_to.generator_list.Count;
foreach (Item block in compare)
{
int block_length = block.generator_list.Count;
int both = 0; //this counts the intersection number
if (compare_to_length < block_length) //to make sure I'm looping
//over the smaller set
{
foreach (string word in compare_to.generator_list)
{
if (block.generator_list.Contains(word))
{
both = both + 1;
}
}
}
else
{
foreach (string word in block.generator_list)
{
if (compare_to.generator_list.Contains(word))
{
both = both + 1;
}
}
}
// I'd like to store the intersection number, both,
// somewhere so I can effectively use parallel loops
}
}
And finally, my Parallel forloop is:
Parallel.ForEach(items, (kk, state, index) => Relatedness2(items, kk));
Any suggestions?
Maybe something like this
public Dictionary<int, int> Relatedness2(IList<Item> compare, Item compare_to)
{
int compare_to_length = compare_to.generator_list.Count;
var intersectionData = new Dictionary<int, int>();
foreach (Item block in compare)
{
int block_length = block.generator_list.Count;
int both = 0;
if (compare_to_length < block_length)
{
foreach (string word in compare_to.generator_list)
{
if (block.generator_list.Contains(word))
{
both = both + 1;
}
}
}
else
{
foreach (string word in block.generator_list)
{
if (compare_to.generator_list.Contains(word))
{
both = both + 1;
}
}
}
intersectionData[block.index] = both;
}
return intersectionData;
}
And
List<Item> items = new List<Item>(8000);
//add to list
var dictionary = new ConcurrentDictionary<int, Dictionary<int, int>>();//thread-safe dictionary
var readOnlyItems = items.AsReadOnly();// if you sure you wouldn't modify collection, feel free use items directly
Parallel.ForEach(readOnlyItems, item =>
{
dictionary[item.index] = Relatedness2(readOnlyItems, item);
});
I assumed that index unique.
i used a dictionaries, but you may want to use your own classes
in my example you can access data in following manner
var intesectiondata = dictionary[1]//dictionary of intersection for item with index 1
var countOfintersectionItemIndex1AndItemIndex3 = dictionary[1][3]
var countOfintersectionItemIndex3AndItemIndex7 = dictionary[3][7]
don't forget about possibility dictionary[i] == null
Thread safe collections is probably what you are looking for http://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx.
When working in multithreaded environment, you need to make sure that
you are not manipulating shared data at the same time without
synchronizing access.
the .NET Framework offers some collection classes that are created
specifically for use in concurrent environments, which is what you
have when you're using multithreading. These collections are
thread-safe, which means that they internally use synchronization to
make sure that they can be accessed by multiple threads at the same
time.
Source: Programming in C# Exam Ref 70-483, Objective 1.1: Implement multhitreading and asynchronous processing, Using Concurrent collections
Which are the following collections
BlockingCollection<T>
ConcurrentBag<T>
ConcurrentDictionary<T>
ConcurentQueue<T>
ConcurentStack<T>
If your Item's index is contiguous and starts at 0, you don't need the Item class at all. Just use a List< HashSet< < string>>, it'll take care of indexes for you. This solution finds the intersect count between 1 item and the others in a parallel LINQ. It then takes that and runs it on all items of your collection in another parallel LINQ. Like so
var items = new List<HashSet<string>>
{
new HashSet<string> {"1", "2"},
new HashSet<string> {"2", "3"},
new HashSet<string> {"3", "4"},
new HashSet<string>{"1", "4"}
};
var intersects = items.AsParallel().Select( //Outer loop to run on all items
item => items.AsParallel().Select( //Inner loop to calculate intersects
item2 => item.Intersect(item2).Count())
//This ToList will create a single List<int>
//with the intersects for that item
.ToList()
//This ToList will create the final List<List<int>>
//that contains all intersects.
).ToList();

Iterating over a growing dictionary

I am working with C# and I have a dictionary called intervalRecordsPerObject of type Dictionary<string, List<TimeInterval>>. I need to iterate through the dictionary. The problem is: everytime I iterate through the dictionary, more KeyValuePairs may get added to it. As the dictionary grows, I need to keep iterating over the new entries too.
Firstly, I did this: A simple foreach loop that gave me an InvalidOperationException saying
Collection was modified; enumeration operation may not execute.
I know I cannot iterate over the Dictionary this way if it keeps changing as C# converts it with ToList() before foreach loop.
I know I can copy the keys to a temporary array, iterate over the dictionary using simple for loop and Count and whenever a new entry is added to the dictionary, add the corresponding key to the array too. Now, the problem is a simple array cannot grow dynamically and I don't know beforehand what the required size could be.
To move ahead, I thought I'd do this:
List<string> keyList = new List<string>(intervalRecordsPerObject.Count);
intervalRecordsPerObject.Keys.CopyTo(keyList.ToArray(), 0);
I cannot do this either. keyList is currently empty and therefore keyList.toArray() returns an array of length 0 which gives me an ArgumentException saying
Destination array is not long enough to copy all the items in the
collection. Check array index and length.
I am stuck! Any idea what more can I try? Thanks for any help.
Addition 1:
The dictionary stores the time intervals for which a particular object is present. Key is the ID of the object. New entries may get added in every iteration (worst case) or may not get added even once. Whether or not entries are added is decided by a few conditions (whether the object overlaps with some other intervals, etc.). This triggers a change in the ID and the corresponding interval list which is then added as a new entry to the dictionary.
Something like this:
List<string> keys = dict.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
var key = keys[i];
List<TimeInterval> value;
if (!dict.TryGetValue(key, out value))
{
continue;
}
dict.Add("NewKey", yourValue);
keys.Add("NewKey");
}
The trick here is that you enumerate the List<T> by index! In this way, even if you add new elements, the for (...) will "catch" them.
Other possible solution, by using a temporary Dictionary<,>:
// The main dictionary
var dict = new Dictionary<string, List<TimeInterval>>();
// The temporary dictionary where new keys are added
var next = new Dictionary<string, List<TimeInterval>>();
// current will contain dict or the various instances of next
// (multiple new Dictionary<string, List<TimeInterval>>(); can
// be created)
var current = dict;
while (true)
{
foreach (var kv in current)
{
// if necessary
List<TimeInterval> value = null;
// We add items only to next, that will be processed
// in the next while (true) cycle
next.Add("NewKey", value);
}
if (next.Count == 0)
{
// Nothing was added in this cycle, we have finished
break;
}
foreach (var kv in next)
{
dict.Add(kv.Key, kv.Value);
}
current = next;
next = new Dictionary<string, List<TimeInterval>>();
}
You can access the Keys by positions rather than by content and use a normal For loop (allowing additions/removals without any restriction).
for (int i = 0; i < dict.Keys.Count; i++)
{
string curKey = dict.Keys.ElementAt(i);
TimeInterval curVal = dict.Values.ElementAt(i);
//TimeInterval curVal = dict[curKey];
//Can add or remove entries
}

HashSet Iterating While Removing Items in C#

I have a hashset in C# that I'm removing from if a condition is met while iterating though the hashset and cannot do this using a foreach loop as below.
foreach (String hashVal in hashset)
{
if (hashVal == "somestring")
{
hash.Remove("somestring");
}
}
So, how can I remove elements while iterating?
Use the RemoveWhere method of HashSet instead:
hashset.RemoveWhere(s => s == "somestring");
You specify a condition/predicate as the parameter to the method. Any item in the hashset that matches the predicate will be removed.
This avoids the problem of modifying the hashset whilst it is being iterated over.
In response to your comment:
's' represents the current item being evaluated from within the hashset.
The above code is equivalent to:
hashset.RemoveWhere(delegate(string s) {return s == "somestring";});
or:
hashset.RemoveWhere(ShouldRemove);
public bool ShouldRemove(string s)
{
return s == "somestring";
}
EDIT:
Something has just occurred to me: since HashSet is a set that contains no duplicate values, just calling hashset.Remove("somestring") will suffice. There is no need to do it in a loop as there will never be more than a single match.
You can't remove items from a collection while looping over it with an enumerator. Two approaches to solve this are:
Loop backwards over the collection using a regular indexed for-loop (which I believe is not an option in the case of a HashSet)
Loop over the collection, add items to be removed to another collection, then loop over the "to-be-deleted"-collection and remove the items:
Example of the second approach:
HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("one");
hashSet.Add("two");
List<string> itemsToRemove = new List<string>();
foreach (var item in hashSet)
{
if (item == "one")
{
itemsToRemove.Add(item);
}
}
foreach (var item in itemsToRemove)
{
hashSet.Remove(item);
}
I would avoid using two foreach loop - one foreach loop is enough:
HashSet<string> anotherHashSet = new HashSet<string>();
foreach (var item in hashSet)
{
if (!shouldBeRemoved)
{
anotherSet.Add(item);
}
}
hashSet = anotherHashSet;
For people who are looking for a way to process elements in a HashSet while removing them, I did it the following way
var set = new HashSet<int> {1, 2, 3};
while (set.Count > 0)
{
var element = set.FirstOrDefault();
Process(element);
set.Remove(element);
}
there is a much simpler solution here.
var mySet = new HashSet<string>();
foreach(var val in mySet.ToArray() {
Console.WriteLine(val);
mySet.Remove(val);
}
.ToArray() already creates a copy for you. you can loop to your hearts content.
Usually when I want to iterate over something and remove values I use:
For (index = last to first)
If(ShouldRemove(index)) Then
Remove(index)

Categories