All array slots contains last result - c#

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

Related

Removing list index while iterating through the list using nested loops

Hey so I'm trying to execute this bit of code, however it is going out of bounds I would assume due to it trying to execute the first loop at the index that was removed. Does anyone know a way I can execute this code without it going out of bounds?
for (int i = myList1.Count - 1; i >= 0; i--)
{
for (int j = 0; j < myList2.Count - 1; j++)
{
if (myList2[j] != myList1[i])
{
myList1.RemoveAt(i);
}
}
}
Obligitory Linq Answer:
myList1 = myList1.Where(i => !myList2.Contains(i)).ToList();
Basically, instead of looping through them both on your own, you use Linq to do it for you. You set myList1 to be the items matching the where clause of [myList2 does not contain item X].

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");
}

storing lists that can be edited in a loop?

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.

List with string array (List<string[]>)

I have some strange problem where all my string arrays has the same value in the List.
Here is my code:
List<string[]> map_data = new List<string[]>();
string[] map_data_array = new string[11];
for(int i = 0; i < 2000; i++)
{
map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
map_data_array has always different data, I've verified that by placing the break point there and I've checked it.
The problem is that map_data has the value of all elements the same. And this value is the data that comes from function PopulateDataFromFile when the i is 1999.
What I am doing wrong? :/
That only happens if you place the same array into the list. As you did not give the code to PopulateDataFromFile we can only guess what happens. Make sure that the function returns a seperate array created with new each time.
You need to process your data in chunks since PopulateDataFromFile(); looks to be returning all of its data in one go (or as much as the array can fit). Using an extension method, you could do something like this: -
List<string[]> map_data = new List<string[]>();
foreach (var batch in PopulateDataFromFile().Batch(11))
{
map_data.Add((batch.ToArray());
}
Extension method: -
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int batchSize)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / batchSize)
.Select(g => g.Select(x => x.item));
}
PopulateDataFromFile() is returning a String array with the same values.
In the loop everytime you just change the address of map_data_array , so that's why always the value will get changed to the newer data obtained from the method call. Reinitialize the string array everytime will help. It should look something like this
for(int i = 0; i < 2000; i++)
{
string[] map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
or if its confusing for you can you make it simple by
for(int i = 0; i < 2000; i++)
{
map_data.Add(PopulateDataFromFile()); // store to List
}

Categories