0

I saw posts like below which are really hard for me to understand. So I am re-posting it. Sorry if someone feels it's duplicate. I have just simple requirements

C# Joins/Where with Linq and Lambda

I have a class like this

public class Person
{
  public int Id{get;set;}
  public string Name{get;set;}
  public string MailingAddress{get;set;}
}

I have a method like below

public IList<Person> GetNames(IList<int> ids)

This will give me List of persons like below

1 "Sam" ""

2 "Dev" ""

4 "Hummy"

I have another method like below

 public IList<Person> GetMailingAddress(IList<int> ids)

This will give me List of persons like below

1 "" "ABC"

6 "" "TTT"

2 "" "XYZ"

Now I need to merge results of two methods so that I can have my final result like this

1 "Sam" "ABC"

2 "Dev" "XYZ"

UPDATE : I am sorry I didnot clearly give my test data. Please see above my test data

Community
  • 1
  • 1
Ziggler
  • 3,361
  • 3
  • 43
  • 61
  • If you have two lists of the same length with correlated indexes that you want to merge into a single list, the operation you're looking for is [`Zip`](http://msdn.microsoft.com/en-us/library/dd267698(v=vs.110).aspx). For more complex "correlation" you should used [`Join`](http://msdn.microsoft.com/en-us/library/bb534675(v=vs.110).aspx), where you can define logic that constitutes a match. Neither of these are a merge in that they don't modify the initial collections, they are *projections* into a new collection. – Preston Guillot Dec 23 '14 at 22:03
  • Unfortunately, it's not entirely clear from your question what you're trying to do. Why do you have two different methods to return `Person` objects based on ID value? Are you dealing with two different underlying data tables or something? Would it be more practical to create a method that fills in all the class fields in a single operation? Is it possible you will be passing different `ids` lists to each method? The `Zip` method can help, as long as you are always returning objects for exactly the same IDs in the same order in both cases. – Peter Duniho Dec 23 '14 at 22:06
  • @Preston - I cannot use Zip since my two lists are not same length. I used join to solve my issue. Please see below – Ziggler Dec 23 '14 at 22:48
  • @Peter : I have two different methods since both will have two different data sources and they call two different services to get data. I cannot create a single method to fill in all fields. – Ziggler Dec 23 '14 at 22:49

4 Answers4

6

I'm slightly confused by what your methods are returning, if you need to combine the two results to get full Person objects then there are two ways you might be able to get things working.

  1. If you can rely on the same number of objects being returned in the same order, you can try:

    names.Zip(mailingAddresses, (n, m) => new Person
    {
        Id = n.Id,
        Name = n.Name,
        MailingAddress = m.MailingAddress
    });
    
  2. If you can't rely on both of those conditions, you can use a Join:

    names.Join(mailingAddresses, n => n.Id, m => m.Id, (n, m) => new Person
    {
        Id = n.Id,
        Name = n.Name,
        MailingAddress = m.MailingAddress
    });
    

Even though you have those two options, there's a third and better option if you have control over the code that actually gets the objects from the data source. If you know you need those two pieces of data, you should create a single method that queries the datasource a single time to get all of the data rather than querying once per piece of data.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • Please see clearly first method returns me objects with Id and Name. Second one gives me Id and MailingAddress. After getting both results I need to merge them to get Id, Name, and MailingAddress. – Ziggler Dec 23 '14 at 22:04
  • @Preston Guillot - I wrote that comment before the code was posted. I will look into it. – Ziggler Dec 23 '14 at 22:14
  • Can anybody tell me how I can post my code which fixed my problem. I cannot answer my own question. I want to post my code so that if someone who needs it can see it. – Ziggler Dec 30 '14 at 18:16
  • @Ziggler - You should be able to answer your own question but if my example helped your fixed your problem, then it should be the accepted answer. – Justin Niessner Dec 30 '14 at 18:24
  • @Justin. Before posting my answer. I accepted your posting as answer and I voted to it. I voted to your answer on Dec 23. If you guys are talking about Tick mark. I am sorry I didnot click it. I did it now. – Ziggler Dec 30 '14 at 18:28
1

Enumerable.Zip definitely will solve your issue.

Andrei Schneider
  • 3,618
  • 1
  • 33
  • 41
  • I tried with Zip like below but not working. I created names like 1,"Sam" and 2,"Dev". I created address like 1,"ABC" and 4, "XYZ". I used Zip but I am getting it as 1,"Sam","ABC: ( which is correct ) and 2,"Dev", "XYZ" which is wrong. I was unable to post my Zip query. But I used one by Mitja S above. – Ziggler Dec 23 '14 at 22:34
  • Zip didnot work but Justin's. Option 2 Join in above worked for me – Ziggler Dec 23 '14 at 22:45
  • I donot know how this got 3 votes. This one clearly didnot solve my issue. – Ziggler Dec 30 '14 at 18:16
  • Well @Ziggler the question was not clearly formulated at first. Details revealed later. But if you're greedy for rating I will remove the answer. – Andrei Schneider Jan 06 '15 at 20:34
0

You can solve it with Enumerable.Zip and Ordering the Data before:

IEnumerable<Person> list = GetNames(new List<int>()).OrderBy(p => p.Id).Zip(GetMainlingAddress(new List<int>()).OrderBy(p => p.Id), (first, second) => { return new Person() { Id = first.Id, Name = first.Name, MailingAddress = second.MailingAddress }; });
Mitja
  • 863
  • 5
  • 22
  • I created names like 1,"Sam" and 2,"Dev". I created address like 1,"ABC" and 4, "XYZ". I used Zip but I am getting it as 1,"Sam","ABC: ( which is correct ) and 2,"Dev", "XYZ" which is wrong. I was unable to post my Zip query. But I used one by Mitja S above. – Ziggler Dec 23 '14 at 22:40
0

Perform join on those two methods returning values by Lambda style Linq syntax:

var query = GetNames().Join(GetMailingAddress(),
                                    n => n.Id,
                                    e => e.Id,
                                    (n, e) => new { n.Id,n.Name,e.Email});

        foreach (var item in query)
        {
            Console.WriteLine(item.Id + "-" + item.Name +"-"+ item.Email);
        }

Perform join on those two methods returning values by Sql-style Linq syntax:

var query = from n in GetNames()
            join e in GetMailingAddress()
            on n.Id equals e.Id
            select new {n.Id,n.Name,e.Email };
foreach (var item in query)
    {
        Console.WriteLine(item.Id + "-" + item.Name +"-"+ item.Email);
    }

Note:Where GetName() and GetMailingAddress() method returns list of result set.

Asif Iqbal
  • 531
  • 8
  • 28