Nested foreaches into a chained linq expression [duplicate] - c#

This question already has answers here:
LINQ expression instead of nested foreach loops
(7 answers)
Closed 9 years ago.
I don't know if it is even possible to do what I think, but I guess it worths to try :)
Can I combine these two nested foreaches?
foreach ( var dept in curDevice.Personnel.Department.Company.Departments )
{
foreach ( var personnel in dept.Personnels )
{
myPersonnels.Add(personnel);
}
}
I want to turn this nested for each into a chained linq expression. Is is possible? If so how?

Use Enumerable.SelectMany
foreach ( var personnel in
curDevice.Personnel.Department.Company.Departments.SelectMany(x=> x.Personnels))
{
myPersonnels.Add(personnel);
}

Use the SelectMany<TSource, TResult> method:
var allPersonnel = curDevice.Personnel.Department.Company.Departments.SelectMany(dept => dept.Personnels);
// If there is no AddRange method:
foreach (var personnel in allPersonnel)
myPersonnels.Add(personnel);
// If there is an AddRange method:
myPersonnels.AddRange(personnel)

This is personnel preference (see what I did there?), but I like sticking with functional programming if that's what I start with.
You can replace the foreach language construct with the List<T>.ForEach method.
curDevice.Personnel.Department.Company.Departments
.SelectMany(department => department.Personnels)
.ToList()
.ForEach(personnel => myPersonnels.Add(personnel);
Typically, we'd use a shorter argument name for the delegates:
curDevice.Personnel.Department.Company.Departments
.SelectMany(d => d.Personnels)
.ToList()
.ForEach(p => myPersonnels.Add(p);
And, if myPersonnels is just a collection, you can create it outright:
var myPersonnels = curDevice.Personnel.Department.Company.Departments
.SelectMany(d => d.Personnels)
.ToList();
Or, if it's already a List<T>, you can add an IEnumerable<T> to it:
myPersonnels.AddRange(
curDevice.Personnel.Department.Company.Departments
.SelectMany(d => d.Personnels)
);

Ken is almost right, but if I'm right the myPersonnels list is an external one where you want to copy the result of your "query". The first two answers are very readable, but if you want to code it shortly, you can write this:
curDevice.Personnel.Department.Company.Departments
.SelectMany(x => x.Personnels) // selecting the personnels
.ToList()
.Foreach(myPersonnels.Add); // iterate trough the personnels list and copy them into the external list

Related

faster way to find persons not in list A but list B [duplicate]

This question already has answers here:
Using LINQ to remove elements from a List<T>
(14 answers)
c# Remove items in a custom list, based on another List<int>
(4 answers)
Filter List By items in Array (LINQ)
(1 answer)
Closed 1 year ago.
today I use this to get a list of persons that is not in list A but in list B. It works but seem to take a very long time to get the result. Is there a faster way that do the same?
var missingPeople = listofPersons.Where(p => allUsedPersons.All(p2 => p2.Id != p.Id)).ToList();
Your current implementation has O( n * m ) time complexity.
Where n is the cardinality of listofPersons.
Where m is the cardinality of allUsedPersons.
So if you have 500 listofPersons and 200 allUsedPersons your code will take 100,000 checks. That is bad.
This is because Linq's Where will run for every item in listofPersons, and inside your Where you have allUsedPersons.All, which will run the p2.Id != p.Id check for every item in allUsedPersons.
Instead, use a HashSet<T> to build a set of known values in O(n) time - which then lets you perform exists checks in O(1) time.
So if you have 500 listofPersons and 200 allUsedPersons my code below will take only 500 checks.
100,000 vs 500: spot the difference.
HashSet<Int32> allPeopleIds = listofPersons.Select( p => p.Id ).ToHashSet();
List<Person> missingPeople = allUsedPersons
.Where( p => !allPeopleIds.Contains( p.Id ) )
.ToList();
In relational-algebra (or is it relational-calculus?) what you're doing is known as an anti-join and Linq supports it via the Except method, however you would need to define a custom-comparator as Linq doesn't yet have an ExceptBy method (but MoreLinq does, though).
Another option is to provide a custom, reusable IEqualityComparer<Person>:
public class PersonIdComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x?.Id == y?.Id;
}
public int GetHashCode(Person obj)
{
return obj?.Id ?? int.MinValue;
}
}
You can use it for many LINQ methods. In this case you should use it for Except:
var missingPeople = listofPersons.Except(allUsedPersons, new PersonIdComparer()).ToList();
Except is quite efficient since it uses a collection similar to HashSet.
I think below method can help you for improve performance:
List<int> listOfIds = allUsedPersons.select(c => c.Id).ToList();
var missingPeople = listofPersons.Where(r=>!listOfIds.Contains(r.Id));
Putting something like allUsedPersons.All() within predicate will unnecessarily multiply the number of iterations. Prepare the list of required field(here p.id) beforehand and use it inside predicate.
Assuming allUsedPersons is list A, below would be faster
var usedPersonsId = allUsedPersons.Select(p => p.id).ToList();
var missingPeople = listofPersons.Where(p => !usedPersonsId.Contains(p.Id)).ToList();

How to dynamically add Where and Or statements to a Linq query [duplicate]

This question already has answers here:
Dynamic where clause (OR) in Linq to Entities
(2 answers)
Closed 3 years ago.
First time using c# and Linq. I have a string coming in through my route. I want to search a couple of different columns for the values in my string. Assuming I'm splitting each word on a space, foreach one of these items I want to dynamically add a .Where to my linq statement. I'm thinking I may need to dynamically add an .Or as well.
foreach (string q in query)
{
results = results.Where(u => u.Name.Contains(r));
results = results.Where(u => u.Text.Contains(r));
}
I'm used to JS where you could do something like results += results.Where(...) I'm not sure the appropriate way to structure this kind of thing using linq.
edit: here is the entire method for clarity
using (var context = new MessageContext())
{
string[] words = query.Split(" ");
var messages = (from m in context.Messages
join u in context.Users on m.UserId equals u.UserID
select new
{
m.Id,
m.Date,
m.Name,
m.Text,
m.UserId,
u.Image
});
foreach (string word in words)
{
messages = messages.Where(u => u.Name.Contains(word)).Union(messages.Where(u => u.Text.Contains(word)));
return messages.ToList();
}
Linq uses lazy evaluation (the results are not evaluated until you start to iterate over the results, or until you call a method like ToList()). As John pointed out each successive call is really just modifiying the search criteria. therefore in your example
results = results.Where(u => u.Name.Contains(r));
results = results.Where(u => u.Text.Contains(r));
is equivalent to
results = results.Where(u => u.Name.Contains(r)).Where(u => u.Text.Contains(r));
which implies a AND condition. If you want an OR condition you would need to us the Union operator.
results = results.Where(u => u.Name.Contains(r)).Union(results.Where(u => u.Text.Contains(r)));
The benefit of this lazy evaluation is that you can extract a base query and add on additional search criteria, thus simplifying your code.
I hope this helps.
foreach (string q in query)
{
results = results.Where(u => u.Name.Contains(r) || u.Text.Contains(r));
}
Or may be you need to elaborate your question a bit more

Transform foreach to LINQ [duplicate]

This question already has answers here:
LINQ equivalent of foreach for IEnumerable<T>
(22 answers)
Closed 3 years ago.
I have two collections, both contain objects.
First one is IList and the second one is Dictionary.
I need to traverse through IList and if the condition is filled then activate method from the certain object which is stored in Dictionary.
The current situation is like this:
foreach (MyObject mo in MyListOfObjects)
{
if (mo.Active == myStatus.Enabled)
{
DictList[mo.ID].Start();
}
}
So far i've done this:
var r = MyListOfObjects.Where(mo => mo.Active == myStatus.Enabled);
But I have no idea how to include in this DictList[mo.ID].Start();
Not a great use of linq, but you could filter the list using linq then loop through it.
var itemsToStart = MyListOfObjects.Where(mo => mo.Active == myStatus.Enabled)
.Select(mo=> DictList[mo]); //or ToList() if you intend to re-iterate
foreach (var itemToStart in itemsToStart) {
itemToStart.Start();
}
If anything at all, just remove the inner if
foreach (MyObject mo in MyListOfObjects.Where(x => x.Active == myStatis.Enabled))
{
DictList[mo.ID].Start();
}
But that is all you should do - it is perfectly readable and maintainable.
MyListOfObjects.Where(mo => mo.Active == myStatus.Enabled).ToList().ForEach(mo => DictList[mo.ID].Start());
ForEach is found: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.foreach?view=netframework-4.7.2

how to invoke a method for each item using LINQ?

I am trying to get a method invoked for each item in a list while passing that method the list item itself. Basically I can do it the drawn out way but was trying to get it in a concise LINQ statement like so:
var urls = html.DocumentNode.SelectNodes("//a[#href]")
.Select(a => a.Attributes["href"].Value)
.Where(href => !href.StartsWith("mailto:")) // skip emails, find only url links
.ToList();
//.ToList().ForEach(href => getWEbData(href.ToString ()));
foreach (string s in urls) {
getWEbData(s);
}
I could not figure out how to get the .ForEach() in to the LINQ
shorthand or if its possible.
You can't. LINQ functions are designed to not cause side effects. ForEach is designed to cause side effects. Hence, there is no ForEach LINQ function.
See "foreach" vs "ForEach" by Eric Lippert
Don't try to use foreach with Linq. Id adds no values and makes it harder to debug. You can embed the query in the foreach call like so:
foreach (string s in html.DocumentNode
.SelectNodes("//a[#href]")
.Select(a => a.Attributes["href"].Value)
.Where(href => !href.StartsWith("mailto:")))
{
getWEbData(s);
}
Note that ToList() is unnecessary (whether you do the query inside or outside of the foreach)
you can use foreach with Linq but its better to have a constructor i.e. in Select statement take a new class object and make a parameterized constructor of that class an in the constructor you can do whatever you want it is one of the easiest and efficient way.
There is no LINQ .ForEach method, but you can easily write your own:
public static class IEnumerableExtensions {
public static void ForEach<T>(this IEnumerable<T> pEnumerable, Action<T> pAction) {
foreach (var item in pEnumerable)
pAction(item);
}
}
and then
html
.DocumentNode
.SelectNodes("//a[#href]")
.Select(a => a.Attributes["href"].Value)
.Where(href => !href.StartsWith("mailto:")) // skip emails, find only url links
.ForEach(href => getWEbData(href.ToString ()));
or slightly better (although I think href may already be a string):
...
.Select(href => href.ToString())
.ForEach(getWEbData);
Although, as others have indicated, just because you can doesn't necessarily mean you should, but that was not your question.

Design: List storing objects; access information associated with that object [duplicate]

This question already has answers here:
Linq Contains method for a object
(4 answers)
Determine if LINQ Enumerable contains object based on a condition? [duplicate]
(4 answers)
Closed 10 years ago.
I have two classes that are pertinent to what I'm trying to do.
Class 1: Person
Class 2: Personal Profiles
Person has properties such as address, name, phone #, etc...
Inside of my Personal Profiles class I have a List that stores the information for the Person's that have been created.
My question: If I'm trying to find out whether a Person with address: 999 Candy Lane exists within the List do I need to create a a new Person with default's for everything except the specified address and then use that in my .Exists or .Contains? Or should I not be creating a new object just for a searching function.
Why don't you use Linq:
theList.Where(x => x.address == "999 Candy Lane").First();
If you use .net 3.5+ you can use a linq query:
i.e.
var result = (from p in Profiles where p.Address=="bla bla" select p).FirstOrDefault();
The result will be null if no matching person is found.
Given the high probability of missing values it is better to use FirstOrDefault.
Of course First and FirstOrDefault takes a predicate so there is no need to use Where
var result = List.FirstOrDefault(x => x.Address == "999 Candy Lane");
if(result != null)
{
......
}
This is the exact use case for Any.
It is used like this:
if(People.Any(p => p.Address.Equals("999 Candy Lane")))
{
//.....
}
No you don't have to create a new object for searching:
Person person = profiles.PersonsList.Where(p => p.Address == "Address here")
.Select(p => p);
You probably want to use linq and a lambda:
var candyLane = Persons.Where(x => x.Address == "999 Candy Lane").ToList();
What this does is supply a lambda predicate to be used as a selector. Think of it like an anonymous function specified like this:
public bool CandyLaneChecker(x){
return x.Address == "999 Candy Lane";
}
Where the braces and return keyword are replaced by a => and the public bool CandyLaneChecker is omitted because this is an anonymous function. That would leave (x) => x.Address == "999 Candy Lane" and we could've left the parenthesis in our original predicate, but they're not needed.
Alternately, one could use a linq expression such as:
var candyLane = (from p in persons
where p.Profile.Address == "999 Candy Lane"
select p).ToList();
Which looks a lot more like a database query and is easier for some people to understand.
The great part about the linq expression is that you could even use that same expression with XML.

Categories