C# 使用LINQ进行自定义排序

C# 使用LINQ进行自定义排序,c#,linq,sorting,C#,Linq,Sorting,看来我错过了一些琐碎的事情 不管怎么说,它是这样的: var order = new[]{1,3,2}; var foos = new[]{new Foo{Id=1}, new Foo{Id=2}, new Foo{Id=3}}; 如何使用Linq按顺序数组对FOO排序 预期结果: foos == new[]{new Foo{Id=1}, new Foo{Id=3}, new Foo{Id=2}}; 编辑: 订单包含Foo id。对不起,我没提那件事。有时正确地问问题比回答问题更难。:) 好

看来我错过了一些琐碎的事情

不管怎么说,它是这样的:

var order = new[]{1,3,2};
var foos = new[]{new Foo{Id=1}, new Foo{Id=2}, new Foo{Id=3}};
如何使用Linq按顺序数组对FOO排序

预期结果:

foos == new[]{new Foo{Id=1}, new Foo{Id=3}, new Foo{Id=2}};
编辑:

订单包含Foo id。对不起,我没提那件事。有时正确地问问题比回答问题更难。:)

好吧,这个问题对我来说似乎并不完全清楚,所以我将尝试澄清我认为你在问什么:

  • 您有一个ID序列,按所需顺序排列
  • 您有一个对象集合,顺序不正确,但具有相应的ID
  • 您希望以与ID序列相同的顺序获取对象集合
对吗

这将是:

var orderedFoos = from orderedId in order
                  join foo in foos on orderedId equals foo.Id into groups
                  select groups.Single();
你需要一个
加入。。。进入
以验证您在
foos
中没有任何丢失或重复的ID。但是,它不会检测到您是否在
顺序中有丢失或重复的ID
。如果您知道一切都是正确的(即,
foos
中的每个条目在
order
中只有一个条目,反之亦然),那么简单的连接就可以了:

var orderedFoos = from orderedId in order
                  join foo in foos on orderedId equals foo.Id
                  select foo;
可以用点符号表示为:

var orderedFoos = order.Join(foos, order => order, foo => foo.ID, (o, f) => f);

如果我有很多这样的事情要做,我可能会使用id/顺序对的
字典
来进行顺序查找O(1)。请注意,您还需要处理排序中缺少值的情况——我选择将它们移到末尾

var order = new Dictionary<int,int>();
order.Add( 1, 1 );
order.Add( 2, 3 );
order.Add( 3, 2 );

var orderedFoos = foos.OrderBy( f => order.Contains(f.Id) ? order[f.Id] : int.MaxValue );
var order=newdictionary();
订单。添加(1,1);
订单。添加(2,3);
订单。添加(3,2);
var orderedFoos=foos.OrderBy(f=>order.Contains(f.Id)?order[f.Id]:int.MaxValue);

这就是你想要做的吗

foos.OrderBy(f => order[f.Id-1]);

如果您现在在输出上打印ID,您会得到:1,3,2

您可以使用嵌套查询来执行此操作,但是使用
O(n²)
效率很低

如果“order”可能包含“foos”中不存在的ID,则应使用
SingleOrDefault()
。如果
foos
可能包含重复的ID,则应使用
First()
FirstOrDefault()

也许连一个连接都可以,但我不确定它是否能保持顺序

var result = Enumerable.Join(order, foos, o => o, f => f.Id, (o, f) => f);

正如Jon所提到的,只有当输入的格式与我的第一个建议所要求的格式相同时,联接才能正常工作。

我认为这并不能解决问题。我认为OP基本上想要抓取在特定位置具有特定Id的
Foo
s。对不起,Skeet,不能使用.NET 4。0@Arnis:这就是我链接到MoreLINQ的原因-但我误解了这个问题。(我现在使用GroupJoin重写了答案。)
order
数组是否指定了
Foo
s的数组索引或
Id
s?确实-请澄清问题。我已经在我的回答中解释了我认为你的要求,但我只是在猜测。(例如,Dale Halliwell猜测了另一个含义。)连接确实会在LINQ中保持对象的顺序。。。但最终可能会出现重复或缺少行的情况。“var result=order.Select(o=>foos.Single(f=>f.Id==o));”这就是Mehr在开始时发布(并删除)的内容。实际上,在这种情况下,这是一种有效的方法,因为性能根本不是问题。无论如何-谢谢。这些基于连接的答案中的一些会比这个小LINQ使用比较器的大集合要昂贵得多。真的吗??Order应该使用快速排序或其他更好的方法,所以是的,O(n logn),但直觉告诉我连接(在LINQ的情况下是等分连接)必须在O(n^2)左右。。你从哪里得到O(n)?是的,它在做一些假设,OP需要clarification@Dale:它将在“内部”部分创建一个哈希,然后使用该哈希进行匹配。假设我们有一个1-1对应关系(即,我们没有N个具有相同密钥的条目),并且散列是合理的(即,查找是O(1),创建是O(1),每个条目),那么结果是O(N)。谢谢Jon,我不知道!您为什么按订单订购?它已经是那个顺序了。详细说明一下:如果我没有顺序,我必须知道哪个序列(如果有的话)是顺序保留的。在LINQtoObjects中,这个操作符可能定义得很好,但如果它是LINQtoSQL,它就不是了。另外,如果我误读了文档,我可能会通过省略orderby引入一个难以发现的bug(如果我只测试了3项,我假设有1/6的几率意外通过,因为我不知道实现细节)。通过添加orderby,我在不伤害任何人的情况下解决了这些问题。erikkallen:“……不伤害任何人。”除了执行不必要排序的性能含义。性能在这里不是问题,值应该匹配(如果它们不匹配,则例外是可以的),但谢谢。:)
foos.OrderBy(f => order[f.Id-1]);
var result = order.Select(o => foos.Single(f => f.Id == o));
var result = order
    .Select(o => foos.FirstOrDefault(f => f.Id == o))
    .Select(f => f != null);
var result = Enumerable.Join(order, foos, o => o, f => f.Id, (o, f) => f);