按照另一个List<T>的顺序对List<T>进行排序。

3

我的模型是:

class Person
{
    public int Id {get; set; }
    public string Name {get; set; }
}

我有两个集合。我希望像 etalonList 一样对 toBeSortedList 进行排序:

List<Person> etalonList = new List<Person>()
{
     new Person() { Id=10, Name="Jon"},
     new Person() { Id=4, Name="Ben"},
     new Person() { Id=11, Name="Magnus"},
     new Person() { Id=8, Name="Joseph"},
};

List<Person> toBeSortedList = new List<Person>()
{
     new Person() { Id=11, Name="Magnus"},
     new Person() { Id=4, Name="Ben"},                
     new Person() { Id=10, Name="Jon"},
     new Person() { Id=8, Name="Joseph"},
};

我尝试过:

var orderedByIdList = tobeSortedList.OrderBy(x => etalonList.IndexOf(x.Id));

但我遇到了这样的错误:

无法将“int”转换为“SOConsoleApplication.Person”

也许你有其他建议?

1
tobeSortedList.OrderBy(x => etalonList.IndexOf(x.Id)).ToList(); 将tobeSortedList按照etalonList中x.Id的索引顺序排序,并转换为列表。 - CodeConstruct
1
@CodeConstruct 这个也不起作用。 - diiN__________
你必须将其转换为一个类(Person)。 - CodeConstruct
1
@CodeConstruct 很抱歉,你完全误解了问题。仅运行 ToList 并不能修复错误。 - DavidG
1
为什么不直接重复使用etalonList,或者将其复制?按照目前的陈述,对toBeSortedList进行排序最终只会使其保持与etalonList相同的值和顺序。 - Joren
4个回答

3

我会首先从etalonList创建一个字典,以加快排序的速度:

int index;
var etalonDictionary = etalonList.ToDictionary(k => k.Id, v => index++);

然后从字典中找回ID,并将其用于排序:
var sortedList = toBeSortedList.OrderBy(x => etalonDictionary[x.Id]).ToList();

如果你已经在使用哈希表,那么你可以很容易地避免排序。从 toBeSortedList 创建一个字典,然后使用 etalonList.Select(p => dict[p.Id]) - Joren

2
List.IndexOf 接受对象作为参数并返回其索引。您正在传递一个 int 值,即 Id。这不会编译。
您可以重写 Equals+GethashCode 并传递 x 而不是 x.Id。但在这种情况下,我更喜欢使用 List.FindIndex
 var orderedByIdList = toBeSortedList
    .OrderBy(x => etalonList.FindIndex(p => p.Id == x.Id));

这里是覆盖Equals方法的实现,使得可以使用IndexOf函数:
class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if(ReferenceEquals(obj, this))
            return true;
        Person other = obj as Person;
        if (other == null)
            return false;
        return other.Id == this.Id;
    }

    public override int GetHashCode()
    {
        return Id;
    }
}

现在这也可以运行:
var orderedByIdList = toBeSortedList
    .OrderBy(x => etalonList.IndexOf(x));

1
我猜测在etalonList中的某些值不在toBeSortedList中,因为在其他情况下这个问题是没有意义的:
  1. toBeSortedList有一些元素不包括在etalonList中。在这种情况下,问题是未指定的;你该如何对这些新的列表成员进行排序?
  2. toBeSortedList包含与etalonList完全相同的成员。在这种情况下,只需返回etalonList或复制它即可。
一个天真但简单的方法来排序toBeSortedList如下(请注意,我假设情况1不可能出现):
static IEnumerable<T> OrderBy(this IEnumerable<T> list, IEnumerable<T> guideList)
{
    foreach (var member in guideList)
    {
        if (toBeSortedList.Contains(member))
            yield return member;
    }
}

var orderedList = toBeSortedList.OrderBy(etalonList).ToList();

虽然性能一般,但如果列表不是很长,它应该可以胜任。


1
在这种情况下,您根本不需要常规排序。您只需将基准与目标join并投影目标元素即可。如果基准不包含所有目标,则可以在排序序列的开头或结尾连接不存在的项目,最终应用一些额外的顺序。
在两种情况下,您最终都会得到快速的O(N)时间复杂度操作。
以下是将不存在的项放在末尾的LINQ代码:
var orderedByIdList =
    (from a in etalonList join b in toBeSortedList on a.Id equals b.Id select b)
    .Concat
    (from a in toBeSortedList join b in etalonList on a.Id equals b.Id into b where !b.Any() select a)
    .ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接