Switch statement on elements in list - c#

Can I make a dynamics switch statement, i.e if I have a list containing 1,2,3,4,5 rather than manually doing case 1:, case 2: etc can I do it using a for loop as shown below?
The code doesn't work. Visual Studio gives an error saying case has to be a referenced label, I am a beginner.
switch (selectedShow)
{
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
case i:
{
waitingList[waitingList.Count].Show = showList[selectedShow];
break;
}
}
}

It kinda seems you just want:
waitingList[waitingList.Count].Show = showList[selectedShow];

Switch statement is used for making a different operations for different values. thats why the "case" select the value to proceed. In your example only one operation is implemented for any value in your list. So, you don't need to apply "if" statement to check the condition where selectedShow is equal to some item in your list. Preferred way to iterate over list in C# is foreach operation. For example:
foreach (var i in showList)
{
if(i == selectedShow)
{
waitingList.Last().Show = i;
break;
}
}
I also replaced unsafe waitingList.[waitingList.Count] for more clear waitingList.Last() method (you may need to add using System.Linq; at the top of your file)

As already specified, you do not need a case statement. You can write a code similar to the following:
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
if (someBreakConditionFunction())
break;
}

This code segment here:
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
case i:
{
waitingList[waitingList.Count].Show = showList[i];
break;
}
}
makes no sense, for every value of i you will execute the case condition:
instead do:
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
}

switch (selectedShow)
{
//Show list is a list of type Shows
for (int i = 0; i < showList.Count; i+=1)
{
waitingList[waitingList.Count].Show = showList[i];
//Add some condition if you want to break the loop.
if(breakCondition)
break;
}
}

Using a forloop and if statement should work. I can iterate through the list using the for each loop and if the selected show (user selects in terminal) is the current iterated show. Then I can reference the show in the waitingList.
selectedShow = int.Parse(Console.ReadLine());
//Show list is a list of type Shows
for (int i = 0; i <=showList.Count;)
{
if (selectedShow == i)
{
//Count starts from 1 not 0
waitingList[waitingList.Count-1].Show = showList[selectedShow];
break;
}
}

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

C# for loop to delete strings containing 'TJ' deletes only one of the Strings containing it

I am trying to create a method that deletes all Sales objects if they have a material description that includes "TJ" somewhere in the string. My problem is that when I create a List with four Sales, two of which include "TJ" in their description, only one is deleted. The one being deleted is not unique to the first one. if I delete the "TJ" from the first Sale the second Sale is then deleted correctly. Why does this occur? My method, along with the TestCase is below.
/// <summary>
/// This method deletes all sales with a 'TJ' material.
/// </summary>
public void deleteTJ()
{
for (int i = 0; i < salesList.Count; i++)
{
if (salesList[i].material.Contains("TJ"))
{
salesList.Remove(salesList[i]);
Console.WriteLine("FOUND IT");
}
else
{
Console.WriteLine("Not this time");
}
}
}
[TestMethod]
public void TestDeleteTJ()
{
sale1.material = "ABJFSJTJ"; // Contains TJ
sale2.material = "KTJANDKFH"; // Contains TJ
sale3.material = "SADVUAWDJ"; // No TJ
sale4.material = "JTNDBAYK"; // No TJ
testList.Add(sale1);
testList.Add(sale2);
testList.Add(sale3);
testList.Add(sale4);
Modifier modifier = new Modifier(testList);
Assert.IsTrue(testList.Count == 4);
modifier.deleteTJ();
Assert.IsTrue(testList.Count == 2);
Assert.AreEqual("SADVUAWDJ", testList[0]);
Assert.AreEqual("JTNDBAYK", testList[1]);
}
This is because you edit the list during the loop. As soon as you remove one item the count gets changed.
There are two ways out of it - use one of of those:
Looping in reverse order (List.Count-1 to 0):
for (int i = salesList.Count - 1; i >= 0; i--)
{
}
Removing values with Lambdas
salesList.RemoveAll(s => s.material.Contains("TJ"));
When you remove an item from the list all subsequent indexes are decremented.
0: ABJFSJTJ
1: KTJANDKFH
2: SADVUAWDJ
3: JTNDBAYK
When you remove the first index the list now looks like this:
0: KTJANDKFH
1: SADVUAWDJ
2: JTNDBAYK
The loop then increments i to 1, effectively skipping what was previously at index 1.
Your issue stems from your loop.
for(int index = 0; index < collection.Count; index++)
if(collection[index].Contains("TJ")
collection.Remove(...)
So in your code, you are physically removing an index from your collection. The problem though, is you're iterating through the array. So you can solve a couple of ways:
Create a copy of your collection, then remove from the one you aren't iterating through.
You could use Linq, it would remove all the "TJ" contained elements from the collection.
Example 1:
var filter = collection;
for(int index = 0; index < collection.Count; index++)
if(collection[index].Material.Contains("TJ"))
filter.Remove(..);
Example 2:
var filter = collection.Where(item => !item.Material.Contains("TJ"));
Example 3:
var filter = collection.RemoveAll(item => item.Material.Contains("TJ");

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 can I utilize Polymorphism with List objects?

Here's a brief overview of my following problem, in case I am approaching this incorrectly:
I have a list of customers, purchase orders, and invoices all displayed in datagrids. I would like to verify that what is displayed in these grids matches the lists I actually have (this is for an automated testing application). The way I am trying to handle it, is by going through each table, and within that going through each row of objects, and comparing it to the objects in my lists (hence the actual/expected variables).
I have an application that has multiple List objects, for example:
List<PurchaseOrder>
List<Customer>
List<Invoice>
And I want to be able to write a loop that can manipulate each of these lists, rather than having one foreach statement for each list. I have multiple tables that show each object (a PO table, a customer table, etc) and I want to iterate all of them as such:
foreach(Table t in Tables)
{
List<??> tableItems = new List<??>; // Perhaps this should be `object tableItems;`?
switch(t.Name)
{
case "PurchaseOrder":
tableItems = purchaseOrders; // purchaseOrders is a List<PurchaseOrder> object
break;
case "Customer":
tableItems = customers; // List<Customer>
break;
case "Invoice":
tableItems = invoices; // List<Invoice>
break;
default:
break;
}
// Now I want to get the count of items, to loop through
for(int i = 0; i < tableItems.Count; i++)
{
// Do work
}
}
However, I can't figure out the right way to use the polymorphism here. If I make tableItems a List of Objects (List<Object>), I will get the error Cannot convert type of List<PurchaseOrder> to List<Object>.
If I cast tableItems to the Object class, I can't call the .Count field.
EDIT: I understand I could solve this easily by putting my for loop inside each case statement -
case "PurchaseOrder":
tableItems = purchaseOrders;
for(int i = 0; i < tableItems.Count; i++)
{
// Do Work
}
break;
But I would like a way to extract that for loop.
EDIT 2
Here's another example of the code I'm using now and what I would like to do with it but can't quite figure out:
foreach(Table t in Tables)
{
List<??> tableItems = new List<??>; // Perhaps this should be `object tableItems;`?
string expectedItem;
string actualItem = t.rows[currentRow].Cells[column1].Value.ToString();
switch(t.Name)
{
case "PurchaseOrder":
for(int i = 0; i < purchaseOrders.Count; i++)
{
PurchaseOrder p = purchaseOrders[i];
expectedItem = p.POValue1;
}
break;
case "Customer":
for(int i = 0; i < customers.Count; i++)
{
Customer c = customers[i];
expectedItem = c.CustValue1;
}
break;
case "Invoice":
for(int i = 0; i < invoices.Count; i++)
{
Invoice inv = invoices[i];
expectedItem = inv.InvoiceValue1;
}
break;
default:
break;
}
// I would prefer to do something like this:
switch(t.Name)
{
case "PurchaseOrder":
actualItem = purchaseOrders; // List<PurchaseOrder> object
break;
case "Customer":
actualItem = customers; // List<Customer>
break;
case "Invoice":
actualItem = invoices; // List<Invoice>
break;
default:
break;
}
for(int i = 0; i < tableItems.Count; i++)
{
(object) o = tableItems[i];
switch(object)
{
case "PurchaseOrder":
expectedItem = o.POValue1;
break;
case "Customer":
expectedItem = o.CustValue1;
break;
case "Invoice":
expectedItem = inv.InvoiceValue1;
break;
default:
break;
}
}
}
Instead of intermingling code that handles all tables simultaneously, I would solve the problem one table at a time.
Each object type dictates what is displayed in the table. This will always be a tuple of any kind of objects. So it is sufficient if the validation method gets these display objects instead of the source:
//More or less pseudo code
bool Validate(Table table, object[][] displayObjects)
{
for each iItem
for each iColumn
if(table.Rows[iItem].Columns[iColumn].Equals(displayObjects[iItem][iColumn])
//everything is fine
else
//there is a validation error
}
This leaves us with the task to transform any list of objects to its respective list of display objects. This can be performed with LINQ:
Validate(POTable, POList.Select(po =>
new object[] { po.Property1, po.Property2, po.Property3 }).ToArray());
// ...
OP. This seems like an XY question here. Your domain problem is that you would like to automate comparing a DataSet with a collection of collections of objects. You have noted that there are commonalities in the code for comparing PurchaseOrders with the PurchaseOrder table and Invoices with the Invoice table. I suggest you read up on design patterns and see if there is a pattern that would work with your problem.
The Strategy pattern looks to be pretty good for it...
public bool TableCollectionCompare<T>(
Compare<DataRow, T> comparer,
DataTable table,
ICollection<T> objects);
Another pattern would be an abstract class to do the loading of the Table and the collection...
public abstract class TableCollectionComparer<T>
{
protected bool Compare(DataRow row, T item);
public bool Compare(DataTable table, ICollection<T> item)
{
foreach(DataRow row in table.Rows)
{
...
bool result = Compare(row, item);
}
}
}

Switch statement inside a foreach loop - not getting expected results

So I am trying to loop though items that are in a listbox in my application. The list box will allow you to select multiple items to which I have a method tied to each item in the listbox. I have a counter variable incremented each time the loop works.When I use the foreach loop with the switch statement below, it does the first item correct, but then loops through the same item again. I know I am missing something as it is supposed to go to the next item in the listbox and not the same item.
string reportname = lstbxReports.SelectedValue.ToString();
int i = 0;
foreach (var report in reportname)
{
switch (reportname)
{
case "Overview":
{
if (i < 1)
{
PrintOverview(filename);
}
else if (i >= 1)
{
PrintOverviewAppend(filename);
}
break;
}
case "Sources":
{
if (i < 1)
{
PrintSource(filename);
}
else if (i >= 1)
{
PrintSourceAppend(filename);
}
break;
}
}
i++
Any thoughts or suggestions on how I can get the foreach loop to go to the next item in the selected listbox?
Also, this is just a snippet as I have about 11 case items to loop through.
You probably want to switch on report, not reportname.
foreach(string item in listBox.Items)
{
}
?
Depends on how you setup the data source for the listbox though (I'm assuming this is WinForm?). If you created it by adding .Items or using the designer then this will work. However if you've used .DataSource then it wont work.
I'd personally have a
List<string> list = SomeMethodWhereIMakeTheList();
and set that to:
listbox.DataSource = list;
then I wouldn't even have to touch the ListBox to mess with the contents:
list.ForEach(...)
Don't do the print logic in a foreach. Split out the data then print such this (note I changed the name of reportname to reportnames to signify a list of items)
string reportnames = lstbxReports.SelectedValue.ToString();
var firstReport = reportnames.First(); // No error checking here, would use FirstOrDefault with null checks.
if (firstReport == "OverView")
PrintOverview(filename);
else
PrintSource(filename);
// Now print out the rest
reportnames.Skip(1)
.ToList()
.ForEach(rp =>
{
if (rp == "OverView")
PrintOverviewAppend(filename);
else
PrintSourceAppend(filename);
});

Categories