Reputation: 10078
Suppose I have two Lists of locations. First has all available locations:
List<Location> locations = new List<Location> {
new Location { id = 1, address = "1 Main St.", selected = false },
...
}
and another is a List or Array of my locations:
List<int> myLocations = new List<int> { 1, 5, 8 };
(IDs are unpredictable, but my locations are guaranteed to be a subset of all locations).
I want to outer join two lists and get a result with selected = true
for the locations where locations.id eq myLocation
s.
If I use Join() or Zip() then I get inner join, in other words, I lose elements that do not exist in myLocations, and if I use the following -
var result = from loc in locations
join my in myLocations
on loc.id equals my into myloc
from m in myloc.DefaultIfEmpty()
select new Location {
id = loc.id, address = loc.address, selected = true
};
then all locations are marked as selected; not to mention that it looks excessively complex. Is there a way to do what I want without looping through list elements?
Upvotes: 2
Views: 5688
Reputation: 41
CodeNotFound's answer is efficient if it's OK to modify the original Location objects.
If you require something that will act purely as a query with no side effects (which is often more in the spirit of Linq), then the following will work. This is similar to tchelidze's answer, but avoids creating new instances of all the objects that don't match:
locations.Select(l => myLocations.Any(i => i == l.id) ?
new Location {id = l.id, address = l.address, selected = true } : l);
or using query syntax:
from location in locations
select myLocations.Any(i => i == location.id)
? new Location {
id = location.id,
address = location.address,
selected = true }
: location;
Upvotes: 0
Reputation: 205589
You are so close.
In the last query you actually implemented so called left outer join, but the more appropriate for this scenario would be group join.
Since the left outer join in LINQ is actually implemented via group join, all you need is to remove the line
from m in myloc.DefaultIfEmpty()
and use selected = myloc.Any()
like this
var result = from loc in locations
join my in myLocations
on loc.id equals my into myloc
select new Location {
id = loc.id, address = loc.address, selected = myloc.Any()
};
Upvotes: 1
Reputation: 23200
Because you need to set the selected
property for each location
that are already in memory then you can just use the ForEach
extension method like code below :
locations.ForEach(location => location.selected = myLocations
.Any(id => id == location.id)
);
With this code, you are not creating a new instances of Location
in memory like it will be by using projection (select new Location
). The same instance into your locations
are used.
Upvotes: 2
Reputation: 8308
Based on what you state
my locations are guaranteed to be a subset of all locations
you could implement LEFT JOIN
instead of OUTER JOIN
.
Try following
locations.Select(l => new Location
{
id = l.id,
adress = l.adress,
selected = myLocations.Any(ml => ml == l.id)
})
Upvotes: 1