storing lists that can be edited in a loop? - c#

I want to take the strings inside of a list I have and convert them into there proper enums. The code using enum.parse works in doing this. The problem with my code is that it doesn't store dispoFilters after leaving the loop. What would be a good way around this problem?
// Get Disposition enum
if (model.FilterSet.Dispositions != null)
{
List<int> dispoFilters = new List<int>();
for (int i = 0; i < model.FilterSet.Dispositions.Count; i++)
{
dispoFilters.Add((int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), model.FilterSet.Dispositions[i].ToString())));
}
}

Your code doesn't store dispoFilters after leaving the IF statement.
Move your declaration up a bit higher.
List<int> dispoFilters = new List<int>();
if (model.FilterSet.Dispositions != null)
{
for (int i = 0; i < model.FilterSet.Dispositions.Count; i++)
{
dispoFilters.Add((int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), model.FilterSet.Dispositions[i].ToString())));
}
}
Scope is the term used to define how long, and to which parts of code, your variables are available. By declaring the variable inside the if statement, you lost your scope when you exited the if.

Thats is because you have defined dispoFilters inside the if block and it wont be available outside of it. So define it outside the if or to an upper scope based on where you need it. Also note that it is not that it is not available outside the loop (as you have mentioned) it will be available outside the loop but within your if condition.
List<int> dispoFilters = new List<int>();
if (model.FilterSet.Dispositions != null)
{
for (int i = 0; i < model.FilterSet.Dispositions.Count; i++)
{
dispoFilters.Add((int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), model.FilterSet.Dispositions[i].ToString())));
}
}
Based on your code:
if (model.FilterSet.Dispositions != null)
{
List<int> dispoFilters = new List<int>();
for (int i = 0; i < model.FilterSet.Dispositions.Count; i++)
{
dispoFilters.Add((int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), model.FilterSet.Dispositions[i].ToString())));
}
//With in the if condition outside the loop you can still access dispoFilters
}

The other answers are correct, but you don't even have to use a for loop. Here is what you can do:
var dispoFilters = model.FilterSet.Dispositions.ConvertAll(item =>
(int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), item.ToString())));
I'm assming that model.FilterSet.Dispositions is a List; If not, you'll have to use toList() before ConvertAll.

Related

All array slots contains last result

Im pretty sure this has been asked before but I dont know what to search for:
I want to populate an array with what I've found in a lambda expression.
I do this
IEnumerable<CapOrderTimeSlot>[] dummyDates = new IEnumerable<CapOrderTimeSlot>[DayCount];
for (int i=0; i< DayCount; i++)
{
dummyDates[i] = OrderSlots.Where(os => os.ComputedStartDate == FirstDate.AddDays(i));
}
The problem is that every item in the array ends up containing the same items (propably the last result in the loop).
How do I avoid this?
The issue is that this code saves expression in each array element, not a "materialized" collection. At the same time, all saved expressions are "linked" to the same value of i due to closure. That is why you are getting the last value.
Just change it this way:
for (int i=0; i< DayCount; i++)
{
dummyDates[i] = OrderSlots.Where(os => os.ComputedStartDate == FirstDate.AddDays(i)).ToArray();
}
Your loop variable i is captured by Linq method. Try to assign it to a local variable inside loop:
IEnumerable<CapOrderTimeSlot>[] dummyDates = new IEnumerable<CapOrderTimeSlot>[DayCount];
for (int i = 0; i < DayCount; i++)
{
int temp = i;
dummyDates[i] = OrderSlots.Where(os => os.ComputedStartDate == FirstDate.AddDays(temp));
}
There is a nice article explaining this problem

Change List inside a for loop

I'm trying to modify a list inside a for value
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
theList.remove(component);
else
theList.add(component);
}
I get an ArgumentOutOfRangeException with this method.
Is there any method to accomplish this?
It can be solved by iterating backwards and using indexes instead of items:
for (int i = list.Count - 1; i > 0; i--)
{
if(condition)
list.RemoveAt(i);
else
list.Add(component);
}
Some explanation: when you iterating over collection you shouldn't change items in the scope. Iterators will detect that and throw (and in case of foreach you must use copy of list). But in case of using indexes (RemoveAt() method) and when iterating backward you are safe as for next iteration the scope doesn't include deleted items. Add() is adding to the end, therefore new item is never in scope.
I'll add few more solutions, which one is better decide yourself:
Classical foreach with copy first:
foreach(var item in list.ToArray()) // imho `ToArray` is better than `ToList`
if(condition)
list.Remove(item);
else
list.Add(component);
New list as result:
var result = new List<...>();
foreach(var item in list)
result.Add(condition ? component : item); // not sure here, but should give you idea
list = result;
This is also a bad practice to mutate the list while iterating over it.
This is an alternative:
theList.RemoveAll(someCircunstances);
you are getting an out of range exception because indexes start on 0.
as stated above, one solution is to remove 1 from theList.count, and another solution is to initiate i at 1 instead of 0.
think of this: if your list has 1 element in it, the index of that element is 0, if you have 100 elements, the index of your hundreth element is 99.
you are thinking of the list like: [1][2][3], while it's actually [0][1][2]
The problem here is that you are deleting values out of the list and then you iterate throught it again with an index which is already removed -> ArgumentOutOfRangeException
So to solve this i suggest you to split it up to two for loops:
for (int i = theList.Count - 1; i >= 0; i--) {
if(someCircunstances)
theList.remove(component);
}
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
theList.add(component);
}
I am agree with Tamas, that don't mutate the list while iterating , there is another way to achieve your point
List<someType> ToRemove=new List<someType>() ; //some type is same type as theList is
List<someType> ToAdd=new List<someType>();
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
ToRemove.add(component);
else
ToAdd.add(component);
}
theList=((theList.Except(ToRemove)).Concat(ToAdd)).ToList();
Based on the comments, you need to be able to apply the same logic for newly created items.
You need to do something like this:
public void DoIt(List<MyObject> theList)
{
List<MyObject> items_to_remove = new List<MyObject>();
List<MyObject> items_to_add = new List<MyObject>();
for (int i = 0; i < theList.Count; i++)
{
if (someCircunstances)
items_to_remove.Add(....); //Remove some existing item
else
items_to_add.Add(....); //Add a new item
}
if(items_to_remove.Count > 0)
items_to_remove.ForEach(x => theList.Remove(x));
if (items_to_add.Count > 0)
{
DoIt(items_to_add); //Recursively process new objects
theList.AddRange(items_to_add);
}
}
The idea is that you insert the items to add and the items to remove in their own lists.
Then after the iteration, you remove the items that need to be removed.
After that you need to add the items to add. However, before doing that you need to run the same logic on them, and that is the explanation for the recursive call.
Please note that I am using MyObject because I don't know the type of your list. Use whatever type that you are working with.
If you can use the current index of the loop to remove the item from the lst, you can do this easily like so:
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 20).ToList();
var rng = new Random();
for (int i = 0; i < numbers.Count; ++i)
{
if (rng.NextDouble() >= 0.5) // If "someCircumstances"
{
numbers.Add(numbers[i]*2);
}
else
{
// Assume here you have some way to determine the
// index of the item to remove.
// For demo purposes, I'll just calculate a random index.
int index = rng.Next(0, numbers.Count);
if (index >= i)
--i;
numbers.RemoveAt(index);
}
}
Console.WriteLine(string.Join("\n", numbers));
}
}
}
This will also loop over all the numbers added to the end of the list. The value of numbers.Count is recomputed at each iteration, so when it changes, the loop will be extended appropriately.
(Offtopic) BONUS QUESTION: In the above code, what will be the average size of the list when the loop exits? And what would be the maximum size?

how to make loop with a list work, that finds new elements? c# webdriver [duplicate]

I am getting "index out of range" from this loop. But I need to use new elements that loop founds, how do I do that? Please help to fix the problem
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
string[] links = new string[linkCount];
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
links[i] = linksToClick[i].GetAttribute("href");
}
I think that you could refactor your code:
var linkElements = driver.FindElements(By.CssSelector("a[href]")).ToList();
var links = new List<string>();
foreach (var elem in linkElements)
{
links.Add(elem.GetAttribute("href"));
}
If that works, you could simplify the query:
var instantLinks = driver.FindElements(By.CssSelector("a[href]"))
.Select(e => e.GetAttribute("href"))
.ToList();
You can rewrite your code to bypass the for loop:
string[] links = driver.FindElements(By.CssSelector("a[href]")).Select(l => l.GetAttribute("href")).ToArray();
This should also avoid the index out of range problem, and cut down the amount of code you have to write.
First of all i dont see a point in assigning linkstoclick values inside loop... And Reason for error must be that linksToClick list's length is more than that of linkCount.
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
List<string> links = new List<string>();
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
if (linksToClick.Count < i)
links.Add(linksToClick[i].GetAttribute("href"));
}
This might help with the out of range exception.
Doing this allows you to create a list of type: string without having to explicitly define the size of the list
the first one gets all of your elements by tag name ...let's assume 5.
in the loop, your driver get's all the elements by css selector, and you might have a different number here. let's say 4.
then, you might be trying to set the fifth element in a four element array.
boom.
Easiest fix to debug:
int linkCount = driver.FindElements(By.TagName("a")).Count;
string[] links = new string[linkCount];
// WRITE OUT HOM MANY links you have
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
// ASSERT THAT YOU HAVE THE SAME AMOUNT HERE
If (links.Count != linksToClick.Count)
// your logic here
links[i] = linksToClick[i].GetAttribute("href");
}

RemoveAt() not working c#

Even after the RemoveAt() method, my list keeps being the same and I don't even get an error:
foreach (var row in queryCandidates.ToList())
{
try
{
xString = queryCandidates.ToList().ElementAt(i).District;
int.TryParse(xString, out xNumber);
temp = xNumber.Equals(districtNumber);
System.Diagnostics.Debug.Write(temp+ " ");
System.Diagnostics.Debug.Write(i+" ");
if (temp == false)
{
System.Diagnostics.Debug.WriteLine(" i is:"+i);
//not working even when it should
queryCandidates.ToList().RemoveAt(i);
}
}
catch { }
i++;
if (last == i)
{
System.Diagnostics.Debug.WriteLine("before ending loop: ");
return View(queryCandidates.ToList());
}
}
System.Diagnostics.Debug.WriteLine("after ending the loop: ");
return View(queryCandidates.ToList());
ToList() creates a new instance. From this instance you are removing the element. You are not removing the element from the original enumerable.
You should be doing something like this instead:
var candidates = queryCandidates.ToList();
var elementsToRemove = new List<int>();
foreach (var row in candidates)
{
// ...
xString = candidates[i].District;
// ...
if (temp == false)
{
// ...
elementsToRemove.Add(i);
}
}
for(int i = elementsToRemove.Count - 1; i >= 0; --i)
candidates.RemoveAt(elementsToRemove[i]);
return View(candidates);
Please note the use of elementsToRemove. You can't remove the items directly in the loop. This will throw an exception.
Additionally, please note that ToList copies all data. Every single time you call it. It should be obvious that this is not a good idea to do in a loop.
queryCandidates.ToList().RemoveAt(i);
ToList() creates a brand new list, which you then remove an element from, but that list is long gone.
Try:
var newList = queryCandidates.ToList();
for (int i=newList.Count-1; i>=0; i--){
///snip
newList.RemoveAt(i);
Note that I changed your foreach to for (in reverse) because you cannot modify a list while you are iterating over it with foreach.
The ToList() function creates a new List every time you call it. The object is removed from that list, not from the original list. So you should call ToList once before the foreach.
Once you've done that the removeAt() call will work and cause new issues because then you are trying to modify the list from within the foreach loop. So you'll need to rewrite your code in a way which takes the remove out of the loop as well.
Well I'm not exactly sure what Type queryCandidates is, but the reason you are not seeing an update is because you are removing element 'i' from the wrong object. Your ToList() function creates a new object of List type. If you want to keep the change you need to cache that list and use it where you use your original queryCandidates object.
queryCandidates isn't a list.
You're converting it to a list which creates a new instance from which you're removing the item but doesn't affect queryCandidates itself.
You can do:
var queryCandidates myCollection.ToList();
and then
queryCandidates.RemoveAt(i);
What works for me is to remove from the bottom up:
for (int i = list.Count - 1; i > 0; i--)
{
if (list[i][0] == " " || list[i][3] == "0")
list.RemoveAt(i);
}
It makes sense that some items are missed after decreasing the item count.

Array of Lists throws NullReferenceException

I am trying to make a 1d array of lists. I make it like this:
public static List<string>[] words = new List<string>[30];
public static List<string>[] hints = new List<string>[30];
And I call it like this:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}
I just wanted to add that vars is where I keep my public variables and that counter is indeed at 0 when the loop starts.
EDIT
In my haste I forgot to add the question... oops...
Here it is: When I call the add function (or any another function for that matter) it returns a null reference exception. How can I fix this?
I assume you're crashing when attempting to call .Add on your array element. You need to initialize your arrays with valid objects.
for( Int32 i = 0; i < vars.words.Length; ++i )
vars.words[i] = new List<string>();
for( Int32 i = 0; i < vars.hints.Length; ++i )
vars.hints[i] = new List<string>();
Why not just make a List<List<string>>, but yes you can make an array of lists
Using a list of lists, as already recommended, would make you escape your problems,
and it´s much more flexible and handy than your construction.
-> f.i. if the size of your data changes, you don´t have to change the list size, but the array
Here's a one-liner to initialize an array of lists of size 30:
static List<string>[] lists = (from i in Enumerable.Range(0, 30)
select new List<string>()).ToArray();
The problem is that array values are initialized to the default value, and the default value for reference types is null.
default(List<string>) returns null.
So, you'll need to re-initialize the objects in the array before you can access them, otherwise you will get a NullReferenceException.
One way to initialize all the objects in your array up front is to use this Linq statement:
const int sizeOfLists = 5;
List<string>[] lists = Enumerable.Range(0, sizeOfLists)
.Select(i => new List<string>())
.ToArray();
Another option is to initialize and add the sub-lists only when you need them, by using an outer List:
var lists = new List<List<string>>();
// ...
var aSubList = new List<string>();
lists.Add(aSubList);
This is particularly useful if you don't know the size of the outer set of lists up-front, and is still accessible by index.
(This was a comment before, but I made it an answer since many other answers got caught up in the solution and don't describe the problem)
You could initialize the lists right before you use them:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
// new code
if (vars.words[counter] == null) vars.words[counter] = new List<string>();
if (vars.hints[counter] == null) vars.hints[counter] = new List<string>();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}

Categories