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.
Related
I need to analyze a task that starts with the code below but I couldn't figure out what the LINQ part is doing. Any leads are appreciated
foreach (var item in list.GroupBy(x => x.AccountNumber).Select(g => g.First()))
{
...
}
Some roughly-equivalent code (i.e. has the same function, but works slightly differently) would be:
var seenAccountNumbers = new HashSet<int>(); // Or some other data type?
foreach (var item in list)
{
if (seenAccountNumbers.Add(item.AccountNumber))
{
...
}
}
This code is a (somewhat wasteful) way of getting the first item by account number. It's wasteful because there's no reason to group everything before trying to find the first item per group.
The same thing can be implemented with an iterator function by iterating over all items in the input list and keeping track of all the AccountNumber values found so far. When a new one is found, yield it and add it to the tracking list. Or rather, HashSet.
In fact, that's how MoreLinq's DistinctBy operator is implemented :
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
From the method's description:
Returns all distinct elements of the given source, where "distinctness"is determined via a projection and the default equality comparer for the projected type.
If a key is seen multiple times, only the first element with that key is returned.
The question's code can be replaced with :
foreach (var item in list.DistinctBy(x => x.AccountNumber))
{...
}
Create a dictionary, with the AccountNumber as Key, and put all your items from list, in that dictionary. That is about what happens.
You will overwrite items, with the same key, and a randomly last element, will stay in the dictionary. There is no order ensured when using GroupBy, so it doesn't matter if you choose First or Last element at the end, it just has the meaning of "pick one" (random).
var dict = new Dictionary<KeyType, ElementType>();
foreach(var item in list)
if (!dict.ContainsKey(item.AccountNumber))
dict[item.AccountNumber] = item;
You original iteration would now be
foreach(var item in dict.Values)
{
.....
}
To ask for Non-LINQ solution is not so strange, cause LINQ offers never the most performant solution, it's just short writing and fast coding.
Basically I have this code:
public static async Task<bool> SubmitOrdertoBroker(CASOrderModel order, IEnumerable<CASOrderItemModel> modelOrderItems)
{
ObservableCollection<CASOrderItem> casOrderItemModel = new ObservableCollection<CASOrderItem>();
var i = (from m in modelOrderItems select m).ToList();
foreach (dynamic item in modelOrderItems)
{
CASOrderItem orderItem;
orderItem = new CASOrderItem();
orderItem.Createdby = item.Createdby;
orderItem.CreatedDate = item.CreatedDate;
orderItem.ItemMetaPK = item.ItemMetaPK;
orderItem.OrderItem = item.OrderItem;
orderItem.OrderItemID = item.OrderItemID;
orderItem.ParentOrderID = item.ParentOrderID;
orderItem.PrdMainPK = item.PrdMainPK;
orderItem.Quantity = item.Quantity;
orderItem.TacticPkey = item.TacticPkey;
casOrderItemModel.Add(orderItem);
}
return true;
}
The issues are:
1.) The foreach {} block is not iterating, and it just skips the code (even if modelOrderItems has 4 items in it), thereby rendering my casOrderItemModel empty (which I am passing to another code block after this code that supposedly populates the collection).
2.) If I try to convert the IEnumerable to a List, the list doesn't contain any items.
Please let me know how I can fix this issue.
Thank you. :)
Your function takes a parameter IEnumerable<CASOrderItemModel> modelOrderItems on which you call ToList():
var i = (from m in modelOrderItems select m).ToList();
But then you iterate over modelOrderItems, not over i:
foreach (dynamic item in modelOrderItems) { ... }
Evaluating the same enumerable collection twice can result in the second iteration being empty, depending on the source of your collection. Try doing this and remove the unused ToList() line:
foreach (CASOrderItemModel item in modelOrderItems) { ... }
Or if you really want to have that explicit ToList() in there:
foreach (var item in i) { ... }
Finally, since your collection contains a strong typed item CASOrderItemModel, using dynamic makes no sense.
Try changing your IEnumerable<T> with List<T>. My assumption here is that since it is an interface, there has to be an object that holds your list anywhere in the application.
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.
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)
{
...
}
}
Here is some sample code:
IList<MyType> myList1=new List<MyType>();
IList<MyType> myList2=new List<MyType>();
// Populate myList1
...
// Add contents of myList1 to myList2
myList2.Add(myList1); // Does not compile
How do I add the contents of one list to another - is there a method for this?
There's no great built-in way to do this. Really what you want is an AddRange method but it doesn't exist on the IList<T> (or it's hierarchy). Defining a new extension method though for this is straight forward
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> enumerable) {
foreach (var cur in enumerable) {
collection.Add(cur);
}
}
myList2.AddRange(myList1);
If you declare both list types as the concrete List instead of IList, you can use the AddRange method:
List<MyType> myList1=new List<MyType>();
List<MyType> myList2=new List<MyType>();
myList2.AddRange(myList1);
otherwise you could use LINQ to combine the two:
using System.Linq;
IList<MyType> myList1=new List<MyType>();
IList<MyType> myList2=new List<MyType>();
var newList = myList1.Concat(myList2);
Use Enumerablr extension,
myList2=new List<MyType>(myList2.Concat(myList1))
BTW, if you do not populate myList2, you can just create it based on myLis1.
EDIT
I've try to research perfomance for several cases
1) AddRange via Add
List2.AddRange(List1);
public static class AddRangeUtils
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> enumerable)
{
foreach (var cur in enumerable)
{
collection.Add(cur);
}
}
}
2) Concat
List2 = new List<TestClass>(List2.Concat(List1))
3) Predefined Collection Count 1
var thirdList = new List<TestClass>(List2.Count + List1.Count);
foreach (var testClass in List1)
{
thirdList.Add(testClass);
}
foreach (var testClass in List2)
{
thirdList.Add(testClass);
}
List2 = thirdList;
4) Predefined Collection Count 2
var thirdList = new List<TestClass>(List2.Count + List1.Count);
thirdList.AddRange(List1);
thirdList.AddRange(List2);
List2 = thirdList;
Collection's Count is the count of elements for each list, List1 and List2:
And came to such results (with different collection's length)
I used this one line approach:
Array.ForEach(ilist1.ToArray(), x => ilist2.Add(x));
If the run-time type of the second list is List<T>, you can cast to that type and use the AddRange() method.
Otherwise, you have to do it yourself with a loop. Alternatively, you could use linq to create a new list containing the contents of both source lists.