首页 > 解决方案 > 排序列表,其中前任和后继定义排序

问题描述

我需要对 LineItems 列表进行排序,其中在每个 LineItem 中定义了一个前任和后继。

LineItem{
   public int Id {get;set;}
   public int? PredecessorId {get;set;}
   public int? SuccessorId {get;set;}
}

第一个 LineItem 没有 PredecessorId,最后一个 LineItem 没有 SuccessorId。

我以为我可以只实现 IComparable 但是对于 2 个项目,在这种情况下它不可能计算排序。

我将如何对列表进行排序List<LineItem>

编辑

这是一个示例

 List<LineItem> lineItems = new List<LineItem>{
    new LineItem{ Id = 1, PredecessorId = 2,        SuccessorId = 103},
    new LineItem{ Id = 2, PredecessorId = null,     SuccessorId = 1},
    new LineItem{ Id = 103, PredecessorId = 1,      SuccessorId = 5},
    new LineItem{ Id = 4, PredecessorId = 5,        SuccessorId = null},
    new LineItem{ Id = 5, PredecessorId = 103,      SuccessorId = 4},


};

预期的排序是:

2,1,103,5,4

标签: c#sorting

解决方案


您可以在子句中排除具有null值的两项where,对其余项进行排序,它们添加到排序列表中:

var sortedList = lineItems
    .Where(x => x.PredecessorId != null && x.SuccessorId != null)
    .OrderBy(x => x.Id)
    .Prepend(lineItems.FirstOrDefault(x => x.PredecessorId == null))
    .Append(lineItems.FirstOrDefault(x => x.SuccessorId == null))
    .Where(x => x != null).ToList();

Console.WriteLine(string.Join(", ", sortedList.Select(x => x.Id)));

输出是:

2, 1, 3, 5, 4

如果它可能有一个包含两个以上具有null值属性的项目的列表,例如:

var lineItems = new List<LineItem>
{
    new LineItem{ Id = 1, PredecessorId = 2, SuccessorId = 3},
    new LineItem{ Id = 2, PredecessorId = null, SuccessorId = 1},
    new LineItem{ Id = 3, PredecessorId = 1, SuccessorId = 5},
    new LineItem{ Id = 4, PredecessorId = 5, SuccessorId = null},
    new LineItem{ Id = 5, PredecessorId = 3, SuccessorId = 4},
    new LineItem{ Id = 6, PredecessorId = null, SuccessorId = 6},
    new LineItem{ Id = 7, PredecessorId = 7, SuccessorId = null},
};

然后你可以这样做:

var sortedList = lineItems
    .Where(x => x.PredecessorId != null && x.SuccessorId != null)
    .OrderBy(x => x.Id)
    .ToList();

sortedList.InsertRange(0, lineItems
    .Where(x => x.PredecessorId == null).OrderBy(x => x.Id));

sortedList.AddRange(lineItems
    .Where(x => x.SuccessorId == null).OrderBy(x => x.Id));

Console.WriteLine(string.Join(", ", sortedList.Select(x => x.Id)));

或者,创建PrependAppend扩展采用IEnumerable<LineItem>以下类型的方法:

static class Extensions
{
    public static IEnumerable<T> Prepend<T>(
        this List<T> source, IEnumerable<T> element)
    {
        source.InsertRange(0, element);
        return source;
    }
    public static IEnumerable<T> Append<T>(
        this List<T> source, IEnumerable<T> element)
    {
        source.AddRange(element);
        return source;
    }
}

然后得到排序列表如下:

var sortedList = lineItems
    .Where(x => x.PredecessorId != null && x.SuccessorId != null)
    .OrderBy(x => x.Id).ToList()
    .Prepend(lineItems.Where(x => x.PredecessorId is null)
    .OrderBy(x => x.Id)).ToList()
    .Append(lineItems.Where(x => x.SuccessorId is null)
    .OrderBy(x => x.Id)).ToList();

Console.WriteLine(string.Join(", ", sortedList.Select(x => x.Id)));

两个输出都是:

2, 6, 1, 3, 5, 4, 7

为编辑而编辑


仅使用新的扩展方法Append

var sortedList = lineItems
    .Where(x => x.PredecessorId is null &&
    x.SuccessorId != null).ToList()
    .Append(lineItems.Where(x => x.PredecessorId != null &&
    x.SuccessorId != null)).ToList()
    .Append(lineItems.Where(x => x.PredecessorId != null &&
    x.SuccessorId is null)).ToList();

Console.WriteLine(string.Join(", ", sortedList.Select(x => x.Id)));

结果:

2, 1, 103, 5, 4

推荐阅读