简体   繁体   中英

Joining two lists in LINQ

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?

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.

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

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

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM