6

Hello I have a little problem with assigning property values from one lists items to anothers. I know i could solve it "the old way" by iterating through both lists etc. but I am looking for more elegant solution using LINQ.

Let's start with the code ...

class SourceType
{
    public int Id;
    public string Name;
    // other properties
}

class DestinationType
{
    public int Id;
    public string Name;
    // other properties
}

List<SourceType> sourceList = new List<SourceType>();
sourceList.Add(new SourceType { Id = 1, Name = "1111" });
sourceList.Add(new SourceType { Id = 2, Name = "2222" });
sourceList.Add(new SourceType { Id = 3, Name = "3333" });
sourceList.Add(new SourceType { Id = 5, Name = "5555" });

List<DestinationType> destinationList = new List<DestinationType>();
destinationList.Add(new DestinationType { Id = 1, Name = null });
destinationList.Add(new DestinationType { Id = 2, Name = null });
destinationList.Add(new DestinationType { Id = 3, Name = null });
destinationList.Add(new DestinationType { Id = 4, Name = null });

I would like to achieve the following:

  • destinationList should be filled with Names of corresponding entries (by Id) in sourceList
  • destinationList should not contain entries that are not present in both lists at once (eg. Id: 4,5 should be eliminated) - something like inner join
  • I would like to avoid creating new destinationList with updated entries because both lists already exist and are very large, so no "convert" or "select new".

In the end destinationList should contain:

1 "1111"
2 "2222"
3 "3333"

Is there some kind of elegant (one line Lambda? ;) solution to this using LINQ ?

Any help will be greatly appreciated! Thanks!

5
  • 4
    LINQ tends to return a new result set as it follows a query pattern, as such it is not typically used to update the sequences it is querying. Commented Mar 14, 2012 at 18:54
  • SO, to clarify, you want this process to alter destinationList to a) copy the Name from sourceList where it already exists and b) remove any item in destinationList that does not have an id in sourceList? Commented Mar 14, 2012 at 18:55
  • 1
    If your lists are very large and you want to use inne-join semantics, consider using something else than a list. You need to index both lists by Id or else joining them will be extremely slow Commented Mar 14, 2012 at 19:01
  • 2
    Searching a list is going to be expensive, and the runtime for your algorithm is going O(N^2). For each item in your first list (N), you have to find the item in the second list who's id matches. (N) If this is something you plan on doing for more than a few items, I would consider a different data structure that is optimized for searching. Commented Mar 14, 2012 at 19:03
  • @JamesMichaelHare - Yes, or maybe the other way around, first somehow join by Id and then assign the name. Commented Mar 14, 2012 at 19:18

5 Answers 5

9

I would just build up a dictionary and use that:

Dictionary<int, string> map = sourceList.ToDictionary(x => x.Id, x => x.Name);

foreach (var item in destinationList)
    if (map.ContainsKey(item.Id))
        item.Name = map[item.Id];

destinationList.RemoveAll(x=> x.Name == null);
Sign up to request clarification or add additional context in comments.

2 Comments

Hmm, yes, this looks very promising! You are right, performance is at stake here. Seems like the best answer to my problem, I'll explore it further. Thanks!
OK, this is it. Not exactly what I imagined but it works perfectly. Thank you!
8

Hope this will your desired result. First join two list based on key(Id) and then set property value from sourceList.

        var result = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) =>
        {
            d.Name = s.Name;
            return d;
        }).ToList();

Comments

4

Barring the last requirement of "avoid creating new destinationList" this should work

var newList = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) => s);

To take care of "avoid creating new destinationList", below can be used, which is not any different than looping thru whole list, except that it probably is less verbose.

destinationList.ForEach(d => {
                               var si = sourceList
                                           .Where(s => s.Id == d.Id)
                                           .FirstOrDefault();
                               d.Name = si != null ? si.Name : "";
                             });
destinationList.RemoveAll(d => string.IsNullOrEmpty(d.Name));

8 Comments

This is O(n^2) - use a dictionary for lookup instead
@BrokenGlass, you mean O(n). The dictionary lookup should be much faster even with insertion. Ideally these lists should probably be dictionary to begin with (assuming Id are unique).
@amit_g - I experimented with join, very interesting, but i couldn't further assign the corresponding name. The second is also interesting...
What do you mean by "i couldn't further assign the corresponding name."? The first one is selecting the source that already has Name in it. Please note that these are not the most efficient way of doing it if the lists are very long. If you have very long lists, use answer posted by @BrokenGlass or at least perform benchmarks.
@amit_g: I didn't mean O(n) - your code is O(n^2) since you do a Foreach = O(n) and a nested Where(..) which is also O(n) -> so it's O(n^2)
|
0

Frankly, this is the simplest:

var dictionary = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach(var item in desitnationList) {
    if(dictionary.ContainsKey(item.Id)) {
        item.Name = dictionary[item.Id];
    }
}
destinationList = destinationList.Where(x => x.Name != null).ToList();

You could do something ugly with Join but I wouldn't bother.

Comments

0

I hope this will be useful for you. At the end, destinationList has the correct data, without creating any new list of any kind.

        destinationList.ForEach(x =>
        {
            SourceType newSource = sourceList.Find(s=>s.Id == x.Id);
            if (newSource == null)
            {
                destinationList.Remove(destinationList.Find(d => d.Id == x.Id));
            }
            else
            {
                x.Name = newSource.Name;
            }
        });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.