Replacing a foreach with a linq statement - c#

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

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

Use LINQ for AddRange to Listbox

Hello I try To Convert My old code to new version with Linq but i have problem for do it
old :
foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
{
if (item.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
{
lstTrace.Items.Add(item.Name);
}
}
to this:
lstTrace.Items.Add(
NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
.FirstOrDefault()
.Name
);
But it just returns one result.
How can I get all found items?
I would not recommend to create 'one-liner' code which spans for 5 lines and mixes both data selection and filling list view. Make both things easy to read and understand. Split (1) retrieving and filtering data with (2) assigning data to list view:
var ethernetInterfaceNames =
from i in NetworkInterface.GetAllNetworkInterfaces()
where i.NetworkInterfaceType == NetworkInterfaceType.Ethernet
select i.Name;
foreach(var name in ethernetInterfaceNames)
lstTrace.Items.Add(name);
I would also move getting ethernet interface names to separate method or layer. Thus you will split business logic and presentation logic. You can use AddRange here, but it willl not make your code any simpler:
lstTrace.Items.AddRange(ethernetInterfaceNames.Select(n => new ListViewItem(n)).ToArray())
I believe simple foreach loop is far more readable.
You need to use the AddRange method to add multiple items and then you just need to use select to get the Names of your nics.
Your current code is using FirstOrDefault which will only ever return a single value (the first) from your enumerable.
lstTrace.Items.AddRange(
NetworkInterface
.GetAllNetworkInterfaces()
.Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
.Select(nic => nic.Name)
.ToArray()
);
Additionally xanatos's comment on your question is worth repeating here. Your previous code worked fine, and was readable. Doing this with LINQ isn't going to make your code faster and I would probably say makes it harder to read if anything. While the above code should work I would seriously consider just keeping your original code.
lstTrace.Items.AddRange(NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet).Select(a => a.Name).ToArray());
You are only getting one, because you are only asking for one: FirstOrDefault will give you the first member of the sequence or the type's default value if its empty.
What you need to do is project the sequence to what you really need using Select:
lstTrace.Items
.AddRange(NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
.Select(item => item.Name));
Here is an example on how to use AddRange
var projects = multi.Read<ProjectDto>();
var projectAct = multi.Read<ProjectActivity>();
foreach (var project in projects)
{
project.ProjectActivities = new List<ProjectActivity>();
project.ProjectActivities.AddRange(projectAct.Where(x => x.ProjectId == project.ProjectId));
}
Note: One cannot use the addrange on a data type IEnumerable only on the data type 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)
}

Using LINQ in Parallel.Foreach

I have following normal foreach using LINQ query, How can i transform it using Parallel.Foreach
foreach (var i in media.Where(x => x is Video)
{
this.Update(i);
}
How can i do it like
Parallel.ForEach(media,i =>
{
//LINQ
});
First of all, Where(x => x is Video) can be replaced by OfType<Video>().
Second, for fluent syntax it's better to use ParallelEnumerable.ForAll extension method:
media.OfType<Video>()
.AsParallel()
.ForAll(this.Update)
The first argument to Parallel.ForEach is an enumerable, so the obvious way would be:
Parallel.ForEach(media.Where(x => x is Video).OrderBy(x => x.Contains("a")), i =>
{
//this.Update(i);
// commented out because you'll probably want to Invoke it
// depending on what it does exactly.
});
You can do either
Parallel.ForEach(media.Where(x => x is Video), this.Update);
or
media.AsParallel().Where(x => x is Video).ForAll(this.Update);
Adding an order in a parallel process makes no sense here.
Try this :
Parallel.ForEach<Video>(media.Where(x => x is Video), i =>
{
this.Update(i);
};
Well ... basically like you've said ... but ... keep in mind that if you need to order the results and you're acting on that, it's not really parallel ...
The linq parallel foreach will partition your collection and work on it at the same time, which you also need to take into account making sure your this.Update can work with multiple users without messing up .
So, what's the question really ?

LINQ expression instead of nested foreach loop

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.

Categories

Resources