querying list within a list using linq - c#

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

Related

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

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.

Remove elements from one List<T> that are found in another

I have two lists
List<T> list1 = new List<T>();
List<T> list2 = new List<T>();
I want remove all elements from list1, which also exist in list2. Of course I can loop through the first loop looking for each element in list2, but I am looking for elegant solution.
Thanks!
To change the actual list1 in place, you could use
list1.RemoveAll(item => list2.Contains(item));
You might instead prefer to simply have a query over the lists without modifying either
var result = list1.Except(list2);
LukeH makes a good recommendation in the comments. In the first version, and if list2 is particularly large, it might be worth it to load the list into a HashSet<T> prior to the RemoveAll invocation. If the list is small, don't worry about it. If you are unsure, test both ways and then you will know.
var theSet = new HashSet<YourType>(list2);
list1.RemoveAll(item => theSet.Contains(item));
With LINQ:
var result = list1.Except(list2);
list1.RemoveAll( item => list2.Contains(item));
Description
I think you mean the generic type List<Type>. You can use Linq to do this
Sample
List<string> l = new List<string>();
List<string> l2 = new List<string>();
l.Add("one");
l.Add("two");
l.Add("three");
l2.Add("one");
l2.Add("two");
l2.Add("three");
l2.Add("four");
l2.RemoveAll(x => l.Contains(x));
More Information
MSDN - List.RemoveAll Method
var result = list1.Except(list2);
Using LINQ you can do this:
List1.RemoveAll(i => !List2.Contains(i));
If you want to remove a list of objects (list2) from another list (list1) use:
list1 = list1.Except(list2).ToList()
Remember to use ToList() to convert IEnumerable<T> to List<T>.
var NewList = FirstList.Where(a => SecondList.Exists(b => b.ID != a.ID));
Using LINQ

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.

Can I use linq to achieve the same thing this foreach loop does?

Here's the c# code that I have:
private double get806Fees (Loan loan)
{
Loan.Fee.Items class806;
foreach (Loan.Fee.Item currentFee in loan.Item.Fees)
{
if (currentFee.Classification == 806) class806.Add(currentFee);
}
// then down here I will return the sum of all items in class806
}
Can I do this using linq? If so, how? I have never used linq and i've read in several places that using linq instead of a foreach loop is faster... is this true?
Similar to some existing answers, but doing the projection in the query, to make the Sum call a lot simpler:
var sum = (from fee in loan.Items.Fees
where fee.Classification == 806
select fee.SomeValueToSum).Sum();
loan.Item.Fees.
Where(x => x.Classification == 806).
Sum(x => x.SomeValueProperty)
Whether it is faster or not is debatable. IMO, both complexities are the same, the non-LINQ version may be faster.
var q =
from currentFee in loan.Item.Fees
where currentFee.Classification == 806
select currentFee;
var sum = q.Sum(currentFee => currentFee.Fee);
private double get806Fees(Loan loan)
{
return load.Item.Fees.
Where(f => f.Classification == 806).
Sum(f => f.ValueToCalculateSum);
}
I'm assuming here that ValueToCalculateSum is also a double. If it's not then you have to convert it before it is returned.
All of the answers so far are assuming that you're summing up loan.Fees. But the code you actually posted calls Items.Add() to add each Item in loan.Fees.Items to an Items object, and it's that Items object (and not loan.Fees, which is also an Items object) that you say you want to sum up.
Now, if Items is just a simple collection class, then there's no need to do anything other than what people are suggesting here. But if there's some side-effect of the Add method that we don't know about (or, worse, that you don't know about), simply summing up a filtered list of Item objects might not give you the results you're looking for.
You could still use Linq:
foreach (Loan.Fee.Item currentFee in loan.Item.Fees.Where(x => x.Classification == 806)
{
class806.Add(currentFee);
}
return class806.Sum(x => x.Fee)
I'll confess that I'm a little perplexed by the class hierarchy implied here, though, in which the Loan.Item.Fees property is a collection of Loan.Fee.Item objects. I don't know if what I'm seeing is a namespace hierarchy that conflicts with a class hierarchy, or if you're using nested classes, or what. I know I don't like it.

Categories

Resources