tags:

views:

251

answers:

3

Preface: I don't understand what this does:

o => o.ID, i => i.ID, (o, id) => o

So go easy on me. :-)


I have 2 lists that I need to join together:

// list1 contains ALL contacts for a customer.
// Each item has a unique ID.
// There are no duplicates.
ContactCollection list1 = myCustomer.GetContacts();

// list2 contains the customer contacts (in list1) relevant to a REPORT
// the items in this list may have properties that differ from those in list1.
/*****/// e.g.:
/*****/        bool SelectedForNotification; 
/*****///  may be different.
ContactCollection list2 = myReport.GetContacts();

I need to create a third ContactCollection that contains all of the contacts in list1 but with the properties of the items in list2, if the item is in the list[2] (list3.Count == list1.Count).


I need to replace all items in list1 with the items in list2 where items in list1 have the IDs of the items in list2. The resulting list (list3) should contain the same number of items at list1.

I feel as though I'm not making any sense. So, please ask questions in the comments and I'll try to clarify.

+4  A: 

Joins are not so difficult, but your problem could probably use some further explanation.

To join two lists, you could do something like

var joined = from Item1 in list1
             join Item2 in list2
             on Item1.Id equals Item2.Id // join on some property
             select new { Item1, Item2 };

this will give an IEnumerable<'a>, where 'a is an anonymous type holding an item from list1 and its related item from list2. You could then choose which objects' properties to use as needed.

To get the result to a concrete list, all that is needed is a call to .ToList(). You can do that like

var list3 = joined.ToList();
// or
var list3 = (from Item1 in list1
             join Item2 in list2
             on Item1.Id equals Item2.Id // join on some property
             select new { Item1, Item2 }).ToList();

To do a left join to select all elements from list1 even without a match in list2, you can do something like this

var list3 = (from Item1 in list1
             join Item2 in list2
             on Item1.Id equals Item2.Id // join on some property
             into grouping
             from Item2 in grouping.DefaultIfEmpty()
             select new { Item1, Item2 }).ToList();

This will give you a list where Item1 equals the item from the first list and Item2 will either equal the matching item from the second list or the default, which will be null for a reference type.

Anthony Pegram
`List3.Count()` should equal `List1.Count()`. How would I go about `select`ing ALL items in `list1`, replacing `Item1` with `Item2` when `Item1.Id == Item2.Id`?
David Murdoch
I'll update to show a "left" join
Anthony Pegram
well well, that is almost exactly what I came up with (I just posted it as an answer)! I'll test yours in a second and get back to ya.
David Murdoch
@David, yours is good. It will give you the second item unless it does not exist, when it will give you the first item. My solution gives you *both* items and allows you to pick and choose properties later. If your needs are met by just having the single item, then you are good to go.
Anthony Pegram
Accepted +1 and thanks!
David Murdoch
A: 

It looks like you don't really need a full-join. You could instead do a semi-join, checking each contact in list 2 to see if it is contained in list 1:

ContactCollection list3 = list2.Where(c => list1.Contains(c));

I don't know how big your lists are, but note that this approach has O(n*m) complexity unless list1 is sorted or supports fast lookups (as in a hashset), in which case it could be as efficient as O(n*log(m)) or rewritten as a merge-join and be O(n).

Michael Petito
there will be at most 10-20 in a list.
David Murdoch
Then this will probably be faster than a naive LINQ join since the LINQ implementation builds a hash-based lookup anyways, and for such few elements the overhead probably is greater.
Michael Petito
`list3.Count()' should equal `list1.Count()`
David Murdoch
+1  A: 

Here is what I came up with (based on this):

        List<Contact> list3 = (from item1 in list1
            join item2 in list2
            on item1.ContactID equals item2.ContactID into g
            from o in g.DefaultIfEmpty()
            select o == null ? item1 :o).ToList<Contact>();

My favorite part is the big nosed smiley

:o)

Thanks for your help!

David Murdoch