yonan2236
yonan2236

Reputation: 13649

Update a property field in a List

I have a List<Map> and I wanted to update the Map.Target property based from a matching value from another List<Map>.

Basically, the logic is:

If mapsList1.Name is equal to mapsList2.Name
    Then mapsList1.Target = mapsList2.Name

The structure of the Map class looks like this:

public class Map {
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Target { get; set; }
}

I tried the following but obviously it's not working:

List<Map> mapsList1 = new List<Map>();
List<Map> mapsList2 = new List<Map>();
    
// populate the 2 lists here
    
mapsList1.Where(m1 => mapsList2.Where(m2 =>  m1.Name == m2.Name) ) // don't know what to do next

The count of items in list 1 will be always greater than or equal to the count of items in list 2. No duplicates in both lists.

Upvotes: 1

Views: 1825

Answers (2)

Caius Jard
Caius Jard

Reputation: 74670

Assuming there are a small number of items in the lists and only one item in list 1 that matches:

list2.ForEach(l2m => list1.First(l1m => l1m.Name == l2m.Name).Target = l2m.Target);

If there are more than one item in List1 that must be updated, enumerate the entire list1 doing a First on list2.

list1.ForEach(l1m => l1m.Target = list2.FirstOrDefault(l2m => l1.Name == l2m.Name)?.Target ?? l1m.Target);

If there are a large number of items in list2, turn it into a dictionary

var d = list2.ToDictionary(m => m.Name);
list1.ForEach(m => m.Target = d.ContainsKey(m.Name) ? d[m.Name].Target : m.Target);

(Presumably list2 doesn't contain any repeated names)

If list1's names are unique and everything in list2 is in list1, you could even turn list1 into a dictionary and enumerate list2:

var d=list1.ToDictionary(m => m.Name);
list2.ForEach(m => d[m.Name].Target = m.Target);

If List 2 has entries that are not in list1 or list1 has duplicate names, you could use a Lookup instead, you'd just have to do something to avoid a "collection was modified; enumeration may not execute" you'd get if you were trying to modify the list it returns in response to a name

mapsList1.Where(m1 => mapsList2.Where(m2 => m1.Name == m2.Name) ) // don't know what to do next

LINQ Where doesn't really work like that / that's not a statement in itself. The m1 is the entry from list1, and the inner Where would produce an enumerable of list 2 items, but it doesn't result in the Boolean the outer Where is expecting, nor can you do anything to either of the sequences because LINQ operations are not supposed to have side effects. The only thing you can do with a Where is capture or use the sequence it returns in some other operation (like enumerating it), so Where isn't really something you'd use for this operation unless you use it to find all the objects you need to alter. It's probably worth pointing out that ForEach is a list thing, not a LINQ thing, and is basically just another way of writing foreach(var item in someList)

Upvotes: 3

Guru Stron
Guru Stron

Reputation: 142993

If collections are big enough better approach would be to create a dictionary to lookup the targets:

List<Map> mapsList1 = new List<Map>();
List<Map> mapsList2 = new List<Map>();
var dict = mapsList2
    .GroupBy(map => map.Name)
    .ToDictionary(maps => maps.Key, maps => maps.First().Target);
foreach (var map in mapsList1)
{
    if (dict.TryGetValue(map.Name, out var target))
    {
        map.Target = target;
    }
}

Note, that this will discard any possible name duplicates from mapsList2.

Upvotes: 3

Related Questions