Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do Linq GroupJoin on self to fill children/parent properties?

Tags:

c#

join

linq

I have the following class

public class OrderItem
{
    public Guid ID { get; set; }
    public Guid? ParentID { get; set; }
    public IEnumerable<OrderItem> Children { get; set; }
    public OrderItem Parent { get; set; }
}

I initially fetch them all from a database and into an array.

OrderItem[] arrOrderItems = ctx.Database.SqlQuery<OrderItem>(orderItemsQuery).ToArray();

Now I want to link up the classes so that my base array still contains ALL the items, but each item has its navigational properties set (Children, Parent).

Are there any Linq functions that provide this functionality?

My inital thought was doing a GroupJoin to fill the children and then a Join to fill the parents. I have my sample GroupJoin code below. I could not figure out how to have the groupjoin return the parent object. It is trying to return IEnumerable < IEnumerable < OrderItem > >

arrOrderItems = arrOrderItems.GroupJoin(arrOrderItems, 
                                        x => x.ID, x => x.ParentID, 
                                        (parent, children) => 
                                        parent.Children = children);

Any thoughts?

Edit :

I just want to find a better (faster) way to do this

foreach(OrderItem item in arrOrderItems)
{
    if (item.ParentID.HasValue)
        item.ParentOrderItem = arrOrderItems.Where(x => x.ID == item.ParentID.Value).FirstOrDefault();

    item.Children = arrOrderItems.Where(x => x.ParentID.HasValue && x.ParentID.Value == item.ID).ToArray();
}
like image 763
Malcolm O'Hare Avatar asked Jan 22 '26 01:01

Malcolm O'Hare


1 Answers

Not sure that assigning the children array inside the GroupJoin is a very good practice but to accomplish what you want you'd just change the code to:

  var result = arrOrderItems.GroupJoin( arrOrderItems,
                                        x => x.ID, x => x.ParentID,
                                        ( parent, children ) => { parent.Children = children; return parent; } );

If you want to reassign arrOrderItems, you'd also have to add a .ToArray() like

  arrOrderItems = arrOrderItems.GroupJoin( arrOrderItems,
                                           x => x.ID, x => x.ParentID,
                                           ( parent, children ) => { parent.Children = children; return parent; } ).ToArray();

I think a better practice would be to separate out the bit of code actually doing the assignments like so

foreach ( var pair in arrOrderItems.GroupJoin( arrOrderItems,
                                               x => x.ID, x => x.ParentID,
                                               ( parent, children ) => Tuple.Create( parent, children ) ) ) {

  var parent = pair.Item1;
  var children = pair.Item2;

  parent.Children = children;
  foreach ( var child in children )
    child.Parent = parent;
  }
like image 51
MerickOWA Avatar answered Jan 25 '26 06:01

MerickOWA



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!