LINQ expression instead of nested foreach loop - c#

So i have List whose each element is a string array
List<string[]> TokenList = new List<string[]>();
I want to display each element in the array for every array in the list. and for that i use a nested foreach loop.
foreach (var temp in pro.TokenList)
{
foreach (var s in temp)
{
Console.WriteLine(s);
}
}
Now i am trying to use LINQ in my programs and i was wondering what kind of LINQ query would be used to achieve the same desired result.

I'd rather keep it simple:
// select all sub-strings of each TokenList into 1 big IEnumerable.
var query = pro.TokenList.SelectMany(item => item);
// display all strings while iterating the query.
foreach(var s in query)
Console.WriteLine(s);
It's funny that people combine many statements, but it will be less readable.

Console.WriteLine(String.Join(Environment.NewLine,
pro.TokenList.SelectMany(s => s)
));
Or,
Console.WriteLine(String.Join(Environment.NewLine,
from arr in pro.TokenList
from s in arr
select s
));

Try to do this:
Console.WriteLine(String.Join(Environment.NewLine,
pro.TokenList.SelectMany(s => s)
));
This should work. If it doesn't add a comment :)

pro.TokenList.ForEach(temp => Array.ForEach(temp, Console.WriteLine));
However not much LINQ here ;), as noted in the comments, just more concise :) Also, as noted by Servy under the other answer - this also has the advantage of not joining and storing all the strings in memory again.

Related

Use a foreach loop within a foreach loop in a linq query

I am wondering on how to do the following. I have the Linq query:
Items items.Where(i => i.GetType() == typeof(SubItem))
.Cast<SubItem>()
.ToList()
.ForEach(i => i.SomeList.Add(i.SomeObject.ForEach(i => i.SomeString)));
My question is about i.SomeList.Add(). I want to return a couple of string values to i.SomeList.Add() from i.SomeObject but I do not know how I can do this in this way? Is it even possible like this to have another ForEach Loop within a Linq ForEach usinq Linq query?
I believe this foreach loop will achieve your goal, if I've understood the problem.
It will loop over any element of items that is a (or is derived from) SubItem. It will then select all SomeObject.SomeString strings and add them to the SomeList.
foreach (var subItem in items.OfType<SubItem>()) {
subItem.SomeList.AddRange(subItem.SomeObject.Select(o => o.SomeString));
}
This is a compilation of suggestions from Panagiotis Kanavos, juharr, and Aluan Haddad.
LINQ isn't really for running Add operations... it's much more powerful when you think of it as returning a resultset.
So instead of
//Add every value of SomeField to targetList
sourceList.ForEach( x => targetList.Add(x.SomeField) )
Think of doing it this way:
//Create a list of all instances of SomeField and assign it to targetList
targetList = sourceList.Select( x => x.SomeField).ToList();
Or if you need to keep the existing items in the target list, do this:
//Create a list of all SomeFields and add it to targetList
targetList.AddRange
(
sourceList.Select( x => x.SomeField )
);
Similarly, instead of using a nested foreach, consider using SelectMany.
I'm not completely clear on your requirements but you probably want something like this:
//To SomeList, add the SomeString field from all instances of SomeObject
someList.AddRange
(
items.OfType<SubItem>().SelectMany( x => x.SomeObject ).Select( x => x.SomeString )
);

List<T> Concatenation dynamically

I am trying to concate List<> as follows-
List<Student> finalList = new List<Student>();
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.Concat(ListFromDict);
}
But no concatenation happens. finalList remains empty. Any help?
A call to Concat does not modify the original list, instead it returns a new list - or to be totally accurate: it returns an IEnumerable<string> that will produce the contents of both lists concatenated, without modifying either of them.
You probably want to use AddRange which does what you want:
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.AddRange(ListFromDict);
Or even shorter (in one line of code):
finalList.AddRange((List<Student>)entry.Value);
And because entry.Value is already of type List<Student>, you can use just this:
finalList.AddRange(entry.Value);
Other answers have explained why Concat isn't helping you - but they've all kept your original loop. There's no need for that - LINQ has you covered:
List<Student> finalList = dictOfList.OrderBy(k => k.Key)
.SelectMany(pair => pair.Value)
.ToList();
To be clear, this replaces the whole of your existing code, not just the body of the loop.
Much simpler :) Whenever you find yourself using a foreach loop which does nothing but build another collection, it's worth seeing whether you can eliminate that loop using LINQ.
You may want to read up the documentation on Enumerable.Concat:
Return Value
Type: System.Collections.Generic.IEnumerable
An IEnumerable that contains the concatenated elements of the two input sequences.
So you may want to use the return value, which holds the new elements.
As an alternative, you can use List.AddRange, which Adds the elements of the specified collection to the end of the List.
As an aside, you can also achieve your goal with a simple LINQ query:
var finalList = dictOfList.OrderBy(k => k.Key)
.SelectMany(k => k.Value)
.ToList();
As specified here, Concat generates a new sequence whereas AddRange actually adds the elements to the list. You thus should rewrite it to:
List<Student> finalList = new List<Student>();
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.AddRange(ListFromDict);
}
Furthermore you can improve the efficiency a bit, by omitting the cast to a List<T> object since entry.Value is already a List<T> (and technically only needs to be an IEnumerable<T>):
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
finalList.AddRange(entry.Value);
}
Concat method does not modify original collection, instead it returns brand new collection with concatenation result. So, either try finalList = finalList.Concat(ListFromDict) or use AddRange method which modifies target list.

querying list within a list using linq

I'm trying to learn linq, but struggling with some concepts. How would I transform this double foreach loop into a linq query please?
foreach (var l1 in list1)
{
foreach (var l2 in list2)
{
if (l1 == l2)
{
list1.Remove(l1);
}
}
}
var list3 = list1.Except(list2);
LINQ does not mutate lists.
If list1 is declared as a List<T>, then you can do this:
list1.RemoveAll(list2.Contains);
You might find that a little difficult to read. The above is essentially equivalent to:
list1.RemoveAll(item1 => list2.Contains(item1));
Note that this solution is not based on LINQ. However, if list2's type does not have a Contains method, then LINQ can help you out with its .Contains extension method; add a using System.Linq; directive to your code in that case.)
P.S.: Please make sure that you have read and understood my above comment: LINQ's purpose is querying for data, not modifying it.
There is number of ways to do it, one example is to use Intersect:
var inBothLists = list1.Intersect(list2);
inBothLists.ToList().ForEach(i => list1.Remove(i));
Linq is for querying, not updating, but you could query for the items that need to be removed and then remove them in a loop:
var itemsToRemove = list1.Where(l2.Contains(l1));
foreach(var item in itemsToRemove)
{
list1.Remove(item)
}

Replacing a foreach with a linq statement

I am trying to convert this code to linq:
foreach (var printer in printers)
{
if (printer.Installed)
installedPrinters.Add(printer);
}
I am new to Linq and would appreciate pointers on how it works when iterating through a collection.
printers.Where(printer => printer.Installed)
.ToList()
.ForEach(printer => installedPrinters.Add(printer));
Note the need to call ToList() before ForEach (see Lambda Expression using Foreach Clause).
Also note that while this works, your original code is probably easier to read... LINQ is cool but don't feel obligated to use it for everything :)
If you are just trying to create a new list, you could always just do:
var installedPrinters = printers.Where(p => p.Installed).ToList();
If you are adding to a list that may already have items in it, then you could try:
installedPrinters.AddRange(printers.Where(p => p.Installed));
Assuming your installedPrinters is actually a collection that supports AddRange such as List.
So first use a Where to filter the Installed==true, then run over them with ForEach:
printers.Where(p => p.Installed).ForEach(p => installedPrinters.Add(p));
foreach (var printer in printers.Where (p => p.Installed) { installedPrinters.Add(printer); }
Try this
printer.Where(x => x.Installed).ToList()
.ForEach(
p=>
{
installedPrinters.Add(p)
}
);

Aggregate methods to use with IEnumerable lists

Is there a way to "convert" (return) an IEnumerable list of, e.g., strings to an IEnumerable list of a different type when that different type accepts the former type in its constructor?
For example, the DataTable.Columns.AddRange() method accepts only lists of columns. Is there a way to return a DataColumn list by offering a string list using LINQ or some sort of aggregate function? I imagine the code would roughly do the following, but in one line:
var columnList = new List<DataColumn>();
foreach (var item in myStringList)
{
columnList.Add(item);
}
return columnList;
Likewise, is there an aggregate method that will take a list and run each of its members against a specific method? For example, I am looking for a one line way to perform the following similar foreach loop:
foreach (var item in myStringList)
{
myDataTable.Columns.Add(item);
}
Obviously, I am looking for generic answers that are not actually dependent on data columns or strings.
You can write
var newList = list.ConvertAll(x => new Something(x));
list.ForEach(x => DoSomething(x));
These methods are defined by th List<T> class.
If you have an arbitrary IEnumerable<T>, you can use LINQ:
var newEnumerable = enumerable.Select(x => new Something(x));
Call Enumerable.Aggregate
List<DataColumn> result = myStringList.Aggregate(
new List<DataColumn>(),
(list, item) => { list.Add(item); return list; }
);
return result;
That said, foreach statement is better.
Yes, in fact, although not all of them are LINQ specific. ForEach is just a List method. For your two examples:
myStringList.ForEach(x => columnList.Add(x));
// assumes myStringList is a List<T>... otherwise convert your enumerable using ToList()
The ForEach method takes an Action and lets you perform some logic on each item. So if you want to do transformations, it's easy enough combining with select:
myStringList.Select(x => new DataColumn(x))
.ToList()
.ForEach(x => columnList.Add(x));
// transforms each element of the string by adding some text, then calling foreach
// on the items
myStringList.ForEach(item => myDataTable.Columns.Add(item));
EDIT: that's not Linq. Sorry, my mistake.

Categories

Resources