I have a problem, I cant reverse the following List:
foreach (List<Foo> row in Items)
{
foreach (Foo item in row.Reverse())
{
...
}
}
I always get the error:
Type void is not enumerable
Whats the problem and how to solve it?
List<T>.Reverse doesn't return anything - it reverses the list in place.
If you want to use the LINQ version of Reverse which returns a reversed sequence but without mutating the existing list, you could use:
foreach (IEnumerable<Foo> row in Items)
{
foreach (Foo item in row.Reverse())
{
...
}
}
Or perhaps more clearly:
foreach (List<Foo> row in Items)
{
// We want to use the LINQ to Objects non-invasive
// Reverse method, not List<T>.Reverse
foreach (Foo item in Enumerable.Reverse(row))
{
...
}
}
List<T>.Reverse() does an in-place reverse. That means it changes your original list.
So, you would use it like this:
foreach (List<Foo> row in Items)
{
row.Reverse();
foreach (Foo item in row)
{
...
}
}
If you don't want to change your original list, you will have to call Enumerable.Reverse explicitly:
foreach (List<Foo> row in Items)
{
foreach (Foo item in Enumerable.Reverse(row))
{
...
}
}
The reason for not being able to use Enumerable.Reverse in the extension method syntax is: Extension methods don't hide / override instance methods and List<T> happens to already have a Reverse method.
List<T>.Reverse() is an in-place reverse, it doesn't return a new list. It changes your orininal list.
Reverses the order of the elements in the entire List<T>.
You need to use row.Reverse(); in your first foreach statement. Like;
foreach (List<Foo> row in Items)
{
row.Reverse();
foreach (Foo item in row)
{
//
}
}
Here is a DEMO.
If you don't want to change your orininal list, you can use Enumerable.Reverse method instead of.
Inverts the order of the elements in a sequence.
foreach (Foo item in Enumerable.Reverse(row))
{
//
}
Here is the same DEMO with using Enumerable.Reverse<T> method.
foreach (List<Foo> row in Items)
{
row.Reverse()
foreach (Foo item in row)
{
...
}
}
Reverse change order within the list - it does not return new list with reversed order of items.
List<T>.Reverse do not return anything all it!
foreach (IEnumerable<Foo> row in Items)
{
row.Reverse();
foreach(Foo item in row)
{
}
}
List.Reverse() is a method with a void signature.
You can probably change your loop as below.
foreach (List<Foo> row in Items)
{
row.Reverse();
foreach (Foo item in row)
{
...
}
}
Related
what I want to do is to add for example class in IEnumerable after specific check
public static IEnumerable<T> GroupBy<T, O> (this IEnumerable<T> list, Func<T, O> filter)
{
List<IEnumerable<T>> grouped = new List<IEnumerable<T>>();
foreach (var item in list)
{
foreach (var g in grouped)
{
foreach (var p in g)
{
if (filter(p).Equals(filter(item)))
{
// Add item to g
}
}
}
}
}
Is something like this possible in foreach?
No, that's not possible: every time you change source collection, foreach has to be started anew. You'll need to have a separate collection for intermediate accumulation, then join that collection with grouped.
Also, your algorithms doesn't work correctly: the second foreach will never start working as on the first iteration there will be no items in grouped collection. So you will just iterate list without applying any logic to its elements.
I have an outer loop iterating over an array of substrings to be matched in the dictionary. In the inner loop I want to iterate over the dictionary and delete an entry whose key contains a substring.How to do this without getting "Collection was modified Exception"?
foreach (string outerKey in new string[] { "PAYERADDR_PAYERNAME", "RECADDR_RECNAME", "PAYERADDR_ADDR", "RECADDR_ADDR" })
{
foreach (var item in _generalWorksheetData.Where(kvp => kvp.Value.Contains(outerKey)).ToList())
{
_generalWorksheetData.Remove(item.Key);
}
}
You need a new collection:
List<string> todelete = dictionary.Keys.Where(k => k.Contains("substring")).ToList();
todelete.ForEach(k => dictionary.Remove(k));
or with a foreach:
foreach (string key in todelete)
dictionary.Remove(key); // safe to delete since it's a different collection
If Dictionary.Keys implemented IList instead of just ICollection you could access it in a backwards for-loop to remove them. But since there is no indexer you can't.
AFAIK, you can't. However you can store those pairs in a list and delete them in a separate loop from the first.
Just update your inner foreach as following:
foreach (var item in _generalWorksheetData.Keys.Where(kvp => kvp.Contains(outerKey)).ToList())
{
_generalWorksheetData.Remove(item);
}
Note that LINQ extension methods ToList and ToArray do allow you to modify collections.
List<string> sampleList = new List<string>();
sampleList.Add("1");
sampleList.Add("2");
sampleList.Add("3");
sampleList.Add("4");
sampleList.Add("5");
// Will not work
foreach (string item in sampleList)
{
sampleList.Remove(item);
}
// Will work
foreach (string item in sampleList.ToList())
{
sampleList.Remove(item);
}
Find match and remove the entries as below
var keysWithMatchingValues = dictionary.Where(d => d.Key.Contains("xyz"))
.Select(kvp => kvp.Key).ToList();
foreach(var key in keysWithMatchingValues)
dictionary.Remove(key);
The question is straightforward. How can I get the last item in a foreach loop? Now I know I can find it starting an index and counting the items in the collection, but I was wondering if there is more elegant way?
foreach (var item in Collection)
{
}
You don't need foreach you can do that with Enumerable.Last
var lastItem = Collection.Last();
MSDN- Enumerable.Last<TSource> Method (IEnumerable<TSource>)
The Last(IEnumerable) method throws an exception if
source contains no elements. To instead return a default value when
the source sequence is empty, use the LastOrDefault method.
Just use Last:
Collection.Last() - This will return the last item in the list.
I would do it with Collection.Last() but you could do:
foreach (var item in Collection.Reverse())
... item is last on the first iteration
but the real question is why would you do this.
I have a winforms TabControl and I am trying to cycle through all the controls contained in each tab. Is there a way to add and in a foreach loop or isn't it possible to evaluate more than one group of items? For example this is what I'd like to do:
foreach (Control c in tb_Invoices.Controls and tb_Statements.Controls)
{
//do something
}
OR
foreach (Control c in tb_Invoices.Controls, tb_Statements.Controls)
{
//do something
}
Is this possible, and if not, what is the next best thing? Do I need to use a for loop?
foreach(TabPage page in yourTabControl.TabPages){
foreach(Control c in page.Controls){
LoopThroughControls(c);
}
}
private void LoopThroughControls(Control parent){
foreach(Control c in parent.Controls)
LoopThroughControls(c);
}
Final solution:
var allControls = from TabPage p in tabControl.TabPages
from Control c in p.Controls
select c;
Original answer - use Concat:
var allControls = tb_Invoices.Controls.Cast<Control>()
.Concat(tb_Statements.Controls.Cast<Control>();
BTW I think it's better to use simple non-generic ArrayList here
ArrayList allControls = new ArrayList();
allControls.AddRange(tb_Invoices.Controls);
allControls.AddRange(tb_Statements.Controls);
What I like to do is:
var list = new List<T>();
list.AddRange(list1);
list.AddRange(list2);
list.AddRange(list3);
list.AddRange(list4);
foreach (T item in list)
{
.....
}
You can do using one foreach loop by writing it recursively. This will ensure to loop through all the controls of all types in your form.
private void LoopAllControls(Control YourObject)
foreach(Control c in YourObject.Controls)
{
if(C.Controls.Count > 0)
LoopAllControls(c.Controls);
//your code
}
You could do:
public static void ForAllChildren(Action<Control> action,
params Control[] parents)
{
foreach(var p in parents)
foreach(Control c in p.Controls)
action(c);
}
Called like:
ForAllChildren(x => Foo(x), tb_Invoices, tb_Statements);
You might be hit a little on performance for the action invocation though in which case you could just use a nested foreach:
foreach (var p in new Control[] { tb_Invoices, tb_Statements })
foreach (Control c in p.Controls)
Foo(c);
Similarly, a generic solution to loop through all items in any non-generic IEnumerable might be (although a bit like using a sledgehammer to drive a nail):
public static void ForEachAll<T>(Action<T> action,
params System.Collections.IEnumerable[] collections)
{
foreach(var collection in collections)
foreach(var item in collection.Cast<T>())
action(item);
}
Called like:
ForEachAll<Control>(x => Foo(x), tb_Invoices.Controls, tb_Statements.Controls);
If you are not in a position to use LINQ (like stuck with .NET2), I suggest you use this method:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] args)
{
foreach (IEnumerable<T> collection in args)
{
foreach (T item in collection)
{
yield return item;
}
}
}
Now you have a generic function that you can use with anything that's enumerable. Your loop can look like this:
foreach (Control c in Concat(tb_Invoices.Controls, tb_Statements.Controls))
{
//do something
}
Simple, cheap and expressive!
EDIT: if your collection do not implement IEnumerable<T> but only IEnumerable, you can add an overload that will accept the latter. Everything stays the same, except that T changes to object in the nested loop.
Hello Functional C# Friends,
So this time i am trying to compact my code and write in more functional , lambda style, as well as i would like to avaoid creating unneccessary lists and classes and let compiler do the work for me. I did manage to convert some small piece of code in functional way but after that i dont have much idea as how to go about.
var errorList = new List<DataRow>();
IEnumerable<DataRow> resultRows = GetResultRows();
resultRows
.Filter(row => row.Field<string>("status").Equals("FAILURE", StringComparison.InvariantCultureIgnoreCase))
.ForEach(row => { errorList.Add(row); });
if (errorList.Count > 0)
{
var excludedBooks = new List<string>();
foreach (DataRow row in errorList)
{
if (ToUseBooksList.Contains((string)row["main_book"]))
{
BookCheckResults.AddRow(string.Format("Error for MainBook {0}, RiskType {1}",
row["main_book"], row["risk_type"]));
if (!excludedBooks.Contains((string)row["main_book"]))
{
excludedBooks.Add((string)row["main_book"]);
}
}
}
}
My Extension methods :
public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (action == null)
throw new ArgumentNullException("action");
foreach (var item in collection)
action(item);
}
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Predicate<T> func)
{
foreach (var item in source)
{
if (func(item))
yield return item;
}
}
I will highly appreciate if you can help me structing this code is more functional, lambda style.
Why on earth did you write you own Filter extension method when Where is available?
The ForEach extension method usually isn't worth the bother.
Eric Lippert has blogged about it, and his philosophical objection to it is that it looks like a side-effect free expression (like most Linq features) but it is actually a side-effecting imperative statement in disguise.
If you want to carry out an action for each item on a list, use the foreach statement. That's what it's for.
If you want to manipulate lists of actions, then you can do that, but then you want IEnumerable<Action>.
For the first part of your code, how about:
var errorList = GetResultRows().Where(row => row.Field<string>("status").Equals("FAILURE", StringComparison.InvariantCultureIgnoreCase)
.ToList();
You have a List<string> called excluded books. Use HashSet<string> instead, and you don't need to check if a string is already added to it:
var excludedBooks = new HashSet<string>();
foreach (DataRow row in errorList)
{
if (ToUseBooksList.Contains((string)row["main_book"]))
{
BookCheckResults.AddRow(string.Format("Error for MainBook {0}, RiskType {1}",
row["main_book"], row["risk_type"]));
excludedBooks.Add((string)row["main_book"]);
}
}
You can also filter the list with Where:
var excludedBooks = new HashSet<string>();
foreach (DataRow row in errorList.Where(r => ToUseBooksList.Contains((string)r["main_book"]))
{
BookCheckResults.AddRow(string.Format("Error for MainBook {0}, RiskType {1}",
row["main_book"], row["risk_type"]));
excludedBooks.Add((string)row["main_book"]);
}