Search code examples
c#linqsql-order-byicomparer

How to order by 2 fields with relation child/parent. Sort it so that child items follow their parents in the sorted list?


I am query in table as below.

Id
BeforeId
Description

For Example:

Id        BeforeId       Description
1         NULL           test
2         NULL           test1
3         2              test2
4         3              test3

If BeforeId is not null will push before Id. I would like to order by Id and BeforeId with sort in the following order.

Id        BeforeId       Description
1         NULL           test
4         3              test3
3         2              test2
2         NULL           test1

I am trying code as below but it is not true.

var listOrder = _entites.Orders.OrderBy(t => t, new CustomComparer()).ToList();

 public class CustomComparer : IComparer<Order>
{
    public int Compare(Order lotA, Order lotB)
    {
        if (lotA.BeforeId!=null)
        {
            if (lotB.Id == lotA.BeforeId)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }
        else if(lotB.BeforeId != null )
        {
            if(lotA.Id == lotB.BeforeId)
            {
                return 1; // A > B
            }
            else
            {
                return 0;
            }

        }
        else
        {
            return 0;
        }
    }
}

Can anyone tell me how to do solve this problem.

Thank you!


Solution

  • The first one I create a view model(add Seq column) and sort it like this

    Id        BeforeId       Description   Seq
    1         NULL           test           1
    2         NULL           test1          2
    3         2              test2          3
    4         3              test3          4
    

    I will auto generate sequence number. After that i will update seq by find each element have before id to sort list again. It may take a lot of time with big data.

    //list order need to sort
    var listNeedToSort = _entites.Order.ToList();
    //list order have before id
    var listBeforeId = listNeedToSort.Where(p=>p.BeforeId!=null).Select(p => p.BeforeId).ToList();
     //count number of duplicate data is not process
     int countLoopDuplicateButNotProcess = 0;
    
      while (listBeforeId.Any())
                    {
                        foreach (var item in listNeedToSort.OrderByDescending(p => p.BeforeId))
                        {
                            if (item.BeforeId != null)
                            {
                                //get record which is mentioned by other record through beforeid.
                                var recordSummary = listNeedToSort.FirstOrDefault(p => p.Id == item.BeforeId);
    
                                if (recordSummary != null)
                                {
                                    // if sequence number of item with before id greater than record which has id equals beforeid
                                    if (item.Seq > recordSummary.Seq)
                                    {
                                        //reset count loop but it process again
                                        countLoopDuplicateButNotProcess = 0;
                                        item.Seq = recordSummary.Seq;
                                        //sort again list
                                        foreach (var item1 in listNeedToSort.Where(p => p.Seq >= recordSummary.Seq && p.Id != item.Id).OrderBy(p => p.Seq))
                                        {
                                            item1.Seq += 1;
                                        }
                                        //remove beforeid in listBeforeId
                                        listBeforeId.Remove(item.BeforeId);
                                    }
                                    else
                                    {
                                        //not process
                                        countLoopDuplicateButNotProcess += 1;
                                    }
                                }
                                else
                                {
                                    //reset count loop but it process again
                                    countLoopDuplicateButNotProcess = 0;
                                      //remove beforeid in listBeforeId
                                    listBeforeId.Remove(item.BeforeId);
                                }
                            }
                            else
                            {
                                //not process
                                countLoopDuplicateButNotProcess += 1;
                            }
                        }
                        //break if not process two times.
                        if (countLoopDuplicateButNotProcess == 2)
                        {
                            break;
                        }
                    }