How to do a foreach loop with multiple lists - c#

I am trying to do a foreach loop that runs through 3 lists. Here is what I have currently:
foreach (Button btn in appList && sequenceLsit && FunctionList)
{
if (btn.FillColor != Color.White)
{
// Do stuff
}
}
I tried using tuples, but as I understand, they use a separate variable for each list. I need a single variable (btn) for all 3 lists.

A foreach loop enumerates one list. If you have three separate lists then you need to create one list from them first, which can then be enumerated. The simplest way to do that is the Enumerable.Concat extension method:
foreach (Button btn in appList.Concat(sequenceLsit).Concat(FunctionList))

There's a few ways to solve this.
You can:
Iterate through each list one at a time (foreach inside foreach inside foreach) as mentioned by John
Union your lists together if they share the same type (how are your lists declared?) and then iterate through them once with a single foreach loop
Also, you can use LINQ to remove the need for the IF statement, like this:
foreach (var btn in appList.Where(x => x.FillColor != Color.White)) {...}

You can use Enumerable.Concat method
foreach (Button btn in appList.Concat(sequenceLsit).Concat(FunctionList))
{
if (btn.FillColor != Color.White)
{
// Do stuff
}
}

#John provided you with .Concat, but you may be able to solve your problem with a different approach:
Create method that works on a button
var CheckButtons(IEnumerable<Button> buttons)
{
foreach (Button btn in buttons)
{
if (btn.FillColor != Color.White)
{
// Do stuff
}
}
}
...
Call it on your lists:
CheckButtons(appList);
CheckButtons(sequenceLsit);
CheckButtons(FunctionList);

Another solution is to install the System.Interactive package (owned by the dotnetfoundation), and use the EnumerableEx.Concat method. It has this signature:
// Concatenates the input sequences.
public static IEnumerable<TSource> Concat<TSource>(
params IEnumerable<TSource>[] sources);
Usage example:
foreach (Button btn in EnumerableEx.Concat(appList, sequenceList, functionList))
{
//...
You can find the source code of this method here. It's pretty trivial.

Expanding on John's answer, if you're going with LINQ, why not go all the way?
foreach (var btn in appList.Concat(sequenceList).Concat(FunctionList)
.Where(b => b.FillColor != Color.White))
{
// do stuff with btn
}

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

Need to add items to list from checkedlistbox and remove some

There's a checklist that contains strings .
There is a for each loop that checks the checked items and then add those items to the list of strings that's called mylist only if they aren't added already.
What i need is to check for the not checked items in the listbox and remove the strings from mylist after unchecking item from box .
Basicaly i have a list called mylist i need to add whatever checked item from checkedboxlist to mylist and whenever i uncheck an item delete the same string from mylist .
Suggest some solutions. Thanks in advance .
Please, be so kind and review your next question (How to Ask)...
Take advantage of the event anyCheckedListBox.ItemCheck (MSDN):
public class Form1 : Form {
ListBox anyListBox;
CheckedListBox anyCheckedListBox;
public Form1() {
anyListBox = new ListBox();
Controls.Add(anyListBox);
anyCheckedListBox = new CheckedListBox();
anyCheckedListBox.Items.Add("test1");
anyCheckedListBox.Items.Add("test2");
anyCheckedListBox.Items.Add("test3");
anyCheckedListBox.ItemCheck += AnyCheckedListBox_ItemCheck;
Controls.Add(anyCheckedListBox);
}
private void AnyCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.CurrentValue == CheckState.Unchecked)
anyListBox.Items.Add(anyCheckedListBox.Items[e.Index]);
else
anyListBox.Items.Remove(anyCheckedListBox.Items[e.Index]);
}
}
Beware that the strings have to be unique with this quick and dirty solution.
The naïve implementation would be to add a call to List.Remove in your loop if the item is not checked. Assuming the code you have is similar to #AustinFrench's comment, something like:
foreach (var box in checkboxList)
{
if (box.IsChecked && !myList.Contains(box.Text))
{
// if it's checked, add it to the list if it's not already there
myList.Add(box.Text);
}
else if (!box.IsChecked)
{
// if it's not checked, try to remove it from the list
myList.Remove(box.Text);
}
}
Note that you do not need to check whether an item exists before calling List.Remove. It will simply return false if the item does not exist.
Additionally note this is an O(n^2) operation. It is potentially checking the entire contents of myList for each item in your checkboxlist. If the lists are long, you may get better performance by sorting the lists first and making a single simultaneous pass through the pair (or at least sort myList so you can search it more efficiently).
Alternatively, consider completely replacing the contents of myList instead. This requires only a single pass through your checkboxlist:
myList.Clear();
foreach (var box in checkboxList)
{
if (box.IsChecked)
myList.Add(box.Text);
}
Or, using LINQ and taking advantage of List.AddRange:
myList.Clear();
myList.AddRange(checkboxList.Where(box => box.IsChecked).Select(box => box.Text));
The below code has a foreach loop that iterates through a list of CheckBoxes and then checks if each item is checked or not. If not checked then gets the index of that entry from myList and uses RemoveAt method to remove that entry from the list using the index.
foreach (var item in checkboxList)
{
if (!item.IsChecked)
{
int index = myList.IndexOf(item);
if(index != -1)
myList.RemoveAt(index);
}
}

Is there a simpler way to preform projection on a ListItemCollection?

G'day all,
Is there a way to preform projection on the contents of a list box.
Specifically I'd like to be able to do it without having to clear and add back the contents of my listbox
This is what I currently have.
public static void SetSelectedWhere(this ListBox listbox, Func<ListItem,bool> condition)
{
var queryableList = listbox.Items.Cast<ListItem>();
queryableList.Select(x=>condition(x)?x.Selected:x.Selected=false);
listbox.Items.Clear();
listbox.Items.AddRange(queryableList.ToArray<ListItem>());
}
and it seems silly to have to clear out my existing collection and add the contents back.
Any thoughts
What about plain old iteration?
foreach (ListItem item in listbox.Items)
{
item.Selected = condition(item);
}
LINQ is not the answer to life the universe and everything. Particularly that part of the universe that involves setting properties on existing objects.
listbox.Items
.Cast<ListItem>()
.Where(x=> condition(x))
.ToList()
.ForEach(item => item.Selected = true);
List<T> has a method called ForEach and you can perform an action for any of the items in the list:
http://msdn.microsoft.com/en-us/library/bwabdf9z.aspx
It is silly to remove and read the items in the collection since it is completely unnecessary.
You should be able to simplify it to the following:
foreach (ListItem item in listbox.Items)) {
item.Selected = condition(item);
}

If ListView Column "x" Contains "value"

This is embedded in another loop, and well, it's pretty slow. Is there a better way to do this?
for(int i=0;i< listView.Items.Count;i++)
{
if(listView.Items[i].SubItems[3].Text == "asdf")
{
}
}
Well there's a nicer way to do it:
foreach (ListViewItem item in listView.Items)
{
if (item.SubItems[3].Text == "asdf")
{
...
}
}
Or you could use LINQ:
var query = listView.Items
.Cast<ListViewItem>()
.Where(item => item.SubItems[3].Text == "asdf");
foreach (var item in query)
{
...
}
I doubt that that will be faster though...
Does your outer loop change the listView? If not, could you do the query once and reuse the results in the outer loop?
In case someone runs across this using WPF, you don't get .SubItems on item when you use foreach (ListViewItem item in listView.Items). Instead, I found I could just use DataRowView and get the value of the cell that way:
foreach (DataRowView drv in listView.Items)
{
if (drv.Row[3].ToString() == "asdf")
{
...
}
}
You do have to add a using System.Data; statement at the top of your class to use it. I found it worked in WPF, and it might in other areas (i.e. WinForms), as well.

foreach loop from multiple arrays c#

This should be a simple question. All I want to know is if there is a better way of coding this. I want to do a foreach loop for every array, without having to redeclare the foreach loop. Is there a way c# projects this? I was thinking of putting this in a Collection...?
Please, critique my code.
foreach (TextBox tb in vert)
{
if (tb.Text == box.Text)
conflicts.Add(tb);
}
foreach (TextBox tb in hort)
{
if (tb.Text == box.Text)
conflicts.Add(tb);
}
foreach (TextBox tb in cube)
{
if (tb.Text == box.Text)
conflicts.Add(tb);
}
You can use LINQ:
conflicts.AddRange(
vert.Concat(hort).Concat(cube)
.Where(tb => tb.Text == box.Text)
);
I'm assuming that conflicts is a List<TextBox>, which has an AddRange method. If it isn't, you'll need to call Add in a (single) loop.
If you're creating conflicts, (or if it starts empty), you can call .ToList() instead.
Another .net 3.5 approach:-
conflicts.AddRange(from textBox in vert.Concat(hort).Concat(cube)
where textBox.Text == box.Text
select textBox);
If you can't use LINQ for whatever reason (and I highly suggest you do) you could make your array searching a single method. For example:
public void FindConflicts(IEnumerable<TextBox> tbList, IList<TextBox> conflicts, string test)
{
foreach(TextBox tb in tbList)
{
if(tb.Text == test)
{
conflicts.Add(tb);
}
}
}
And then call it like so:
FindConflicts(vert, conflicts, box.Text);
FindConflicts(hort, conflicts, box.Text);
FindConflicts(cube, conflicts, box.Text);
There are of course many ways to write this, but you could also do
foreach (var direction in new[] { vert, hort, cube })
foreach (TextBox tb in direction)
if (tb.Text == box.Text)
conflicts.Add(tb);
var unionResult = vert.Concat(hort).Concat(cube)
foreach(TextBox tb in unionResult)
if(tb.Text == box.Text)
conflicts.Add(tb);
You should be able to use Enumerable.Concat to glue them together if you're using .Net 3.5 or higher.
foreach (TextBox tb in vert.Concat(hort).Concat(cube))
If you try to create Sudoku game(mentioned in comments) first read about Permutation group and Combinatorics.
This will help you to choose more efficient Application Model w/o using foreach on text boxes. Using lazy computation resolve the problem with object reduction but not improve your logics man.

Categories