Pascal Berger
Pascal Berger

Reputation: 4342

Invert nested collections in LINQ

Having a list of type A, each containing a list of type B, what's the best way to get a list of all type B, each containing a list of type A to which they belong?

Having a list like the following:

var parents = new List<Parent> {
    {
        new Parent {
            ID = 1,
            Childs = new List<Child> {
                {
                    new Child {
                        ID = 1
                    }
                },
                {
                    new Child {
                        ID = 2
                    }
                },
                {
                    new Child {
                        ID = 3
                    }
                }
            }
        }                    
    },
    {
        new Parent {
            ID = 2,
            Childs = new List<Child> {
                {
                    new Child {
                        ID = 3
                    }
                },
                {
                    new Child {
                        ID = 4
                    }
                },
                {
                    new Child {
                        ID = 5
                    }
                }
            }
        }                    
    }
};

I would like to query this to receive the following result:

[
  {
    Child = 1,
    InParent = [1]
  },
  {
    Child = 2,
    InParent = [1]
  },
  {
    Child = 3,
    InParent = [1, 2]
  },
  {
    Child = 4,
    InParent = [2]
  },
  {
    Child = 5,
    InParent = [2]
  },
]

EDIT: I tried an approach to flatten the childs first using SelectMany & Distinct, but not sure how to link this to the parent again:

var foo =
    from childId in parents.SelectMany(x => x.Childs).Select(x => x.ID).Distinct()
    select
        new
        {
            childId = childId,
            inParent = // Missing Part
        };

Upvotes: 3

Views: 1285

Answers (3)

Tim Schmelter
Tim Schmelter

Reputation: 460158

You have to use SelectMany first to flatten them, then use GroupBy to group by child-id and String.Join to concat each parent-id:

var childParents = parents
    .SelectMany(p => p.Childs.Select(c => new {Parent = p, Child = c}))
    .GroupBy(x => x.Child.ID)
    .Select(g => new
    {
        Child = g.Key,
        InParent = String.Join(", ", g.Select(x => x.Parent.ID))
    });

Result:

enter image description here

If you actually don't want that InParent property is a string but a List<int>(or array) use this:

.....
InParent = g.Select(x => x.Parent.ID).ToList()  // or ToArray()

Upvotes: 8

Srujal Kachhela
Srujal Kachhela

Reputation: 3

I guess you should try changing your data model if you are looking for storing tree like structure, in that scenario you should always use single Linked List with custom object & nested reference to corresponding parent / child in the similar way you store in database.

It is an ideal way to handle data structures of such kind as such otherwise you will end up in many nested queries.

Upvotes: -1

Heinzi
Heinzi

Reputation: 172280

You can split your large problem into two simpler problems:

  1. Create an anonymous object for each parent/child pair, containing a reference to the parent as well as to the child. You can use a simple LINQ query with two from clauses for that.

  2. Group those objects into the representation you need. The group by clause is your friend here.

Upvotes: 0

Related Questions