1

I need to flatten a parent list and two child lists into one list. How can I do this using c# and linq?

Here is my code...

public class Customer
{
    public string FirstName { get; set;}
    public string LastName { get; set;}
    // need to flatten these lists
    public List<CreditCard> CreditCards { get; set;}
    public List<Address> Addresses{ get; set;}
}

//  Customer has CreditCards list and Addresses list
List<Customer> allCustomers = _db.GetAllCustomers();

// how to flatten Customer, CreditCards list, and Addresses list into one flattened record/list?

var result = allCustomers.GroupBy().SelectMany(); // how to flatten nested lists?

So the result list would contain items that look flattened like this:

Joe, Blow, Visa, Master Card, 38 Oak Street, 432 Main Avenue

Sally, Cupcake, Discover, Master Card, 29 Maple Grove, 887 Nut Street

It would flatten the customer FirstName, LastName, CreditCards list, and Addresses list.

Thanks for any feedback!

3
  • 2
    What's the end goal here? It seems that overriding .ToString is what you really want. Commented Aug 24, 2012 at 16:08
  • 1
    "flatten each customer and associated lists into one record in a list" What should be the type of that flattened "record"? Commented Aug 24, 2012 at 16:08
  • 1
    Also define the structure of CreditCard and Address class Commented Aug 24, 2012 at 16:09

5 Answers 5

5

Implement IEnumerable:

public class Customer : IEnumerable<string>
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public List<CreditCard> CreditCards {get; set;}
    public List<Address> Addresses{get; set;}

    public IEnumerator<string> GetEnumerator()
    {
        yield return FirstName;
        yield return LastName;
        foreach (CreditCard c in CreditCards)
        {
            yield return c.ToString();
        }
        foreach (Address a in Addresses)
        {
            yield return a.ToString();
        }
    }
}

...

var result = allCustomers.SelectMany(c => c);

Note: this is just an example.

Sign up to request clarification or add additional context in comments.

Comments

1

Use SelectMany if you want to flatten everything into one list. In this case, you are wanting a record per customer still, so you don't need to flatten.

For an array like your example, something like this should work:

var result = customers
   .Select(customer => new[]
   {
      customer.FirstName,
      customer.LastName
   }
   .Concat(customer.CreditCards.Select(cc => cc.ToString()))
   .Concat(customer.Addresses.Select(address => address.ToString())));

1 Comment

@BAF - exactly what I was after thank you. Samual - this works fine too and I appreciate the source code you provided. I chose Baf simply because I prefer lambda syntax. Kendall - this solution will work too. Thanks for sharing it. L.B I'm a blind programmer so didn't "see" that I forgot to edit the code endentation after my edit. Sorry about that. If interested, google screen reader and read up on wikipedia for how I do what I do. My initial post I pressed ctrl+k to format it. I thought the edit operation would preserve my initial indentation.
0

Cast each type to object and then use Union to flatten them.

        var allCreditCards = from customer in allCustomers
                             from creditCard in customer.CreditCards
                             select (object)creditCard;

        var allAddresses = from customer in allCustomers
                           from address in customer.Addresses
                           select (object)address;

        var flat = allCustomers.Concat(allCreditCards).Concat(allAddresses);

I'm not exactly sure the value in having a flattened IEnumerable<object> when the items are all different types, but that's how you'd do it.

2 Comments

Union gives you the union set. You probably don't want nor need the overhead of the union here. Concat is probably what you want.
@BAF, thanks, you're right. Of course the edit to the question makes my answer no longer apply, but it does what was originally asked. :-)
0

Based on your edit, this will give you an IEnumerable<string> like you want:

var flatenned = from c in allCustomers
                select
                    c.FirstName + ", " +
                    c.LastName + ", " +
                    String.Join(", ", c.CreditCards.Select(c2 => c2.Name).ToArray()) + ", " +
                    String.Join(", ", c.Addresses.Select(a => a.Street).ToArray());

Outputs:

Joe, Blow, Visa, Master Card, 38 Oak Street, 432 Main Avenue

Sally, Cupcake, Discover, Master Card, 29 Maple Grove, 887 Nut Street

Full test code:

using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace Console40 { class LinqFlatten {

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        // need to flatten these lists
        public List<CreditCard> CreditCards { get; set; }
        public List<Address> Addresses { get; set; }
    }

    public class CreditCard
    {
        public string Name { get; set; }
    }

    public class Address
    {
        public string Street { get; set; }
    }

    public static void Test()
    {
        //  Customer has CreditCards list and Addresses list
        List<Customer> allCustomers = GetAllCustomers();

        // how to flatten Customer, CreditCards list, and Addresses list into one flattened record/list?

        var flatenned = from c in allCustomers
                        select
                            c.FirstName + ", " +
                            c.LastName + ", " +
                            String.Join(", ", c.CreditCards.Select(c2 => c2.Name).ToArray()) + ", " +
                            String.Join(", ", c.Addresses.Select(a => a.Street).ToArray());

        flatenned.ToList().ForEach(Console.WriteLine);
    }

    private static List<Customer> GetAllCustomers()
    {
        return new List<Customer>
                   {
                       new Customer
                           {
                               FirstName = "Joe",
                               LastName = "Blow",
                               CreditCards = new List<CreditCard>
                                                 {
                                                     new CreditCard
                                                         {
                                                             Name = "Visa"
                                                         },
                                                     new CreditCard
                                                         {
                                                             Name = "Master Card"
                                                         }
                                                 },
                               Addresses = new List<Address>
                                               {
                                                   new Address
                                                       {
                                                           Street = "38 Oak Street"
                                                       },
                                                   new Address
                                                       {
                                                           Street = "432 Main Avenue"
                                                       }
                                               }
                           },
                       new Customer
                           {
                               FirstName = "Sally",
                               LastName = "Cupcake",
                               CreditCards = new List<CreditCard>
                                                 {
                                                     new CreditCard
                                                         {
                                                             Name = "Discover"
                                                         },
                                                     new CreditCard
                                                         {
                                                             Name = "Master Card"
                                                         }
                                                 },
                               Addresses = new List<Address>
                                               {
                                                   new Address
                                                       {
                                                           Street = "29 Maple Grove"
                                                       },
                                                   new Address
                                                       {
                                                           Street = "887 Nut Street"
                                                       }
                                               }
                           }
                   };
    }
}

}

Comments

0

This uses linq to objects, since it depends on string.Join:

allCustomers.Select(c=>
  new { FirstName = c.FirstName, LastName= c.LastName,
     CardsList = string.Join(",", c.CreditsCards.Select(c=> c.CardName))
     AddressesList = string.Join(",", c.Addresses.Select(c=> c.Street)
  }
)

1 Comment

@Samuel Edited, I forgot a .Select, I expect you to replace "CardName" with the name of the property that you want to appear.

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.