使用LINQ保留订单


Answers:


645

我检查了System.Linq.Enumerable的方法,丢弃了所有返回非IEnumerable结果的方法。我检查了每个注释,以确定结果的顺序与源顺序之间的差异。

绝对保留订单。您可以按索引将源元素映射到结果元素

  • 无数
  • 康卡特
  • 选择
  • 数组
  • 到清单

保留订单。元素被过滤或添加,但不重新排序。

  • 不同
  • 除了
  • 相交
  • 类型
  • 前置(.net 4.7.1中的新功能)
  • 跳跃
  • 跳过而
  • 采取
  • 哪里
  • 邮编(.net 4中的新功能)

销毁订单-我们不知道期望的订单顺序。

  • 到字典
  • 去查查看

明确地重新定义订单-使用这些来更改结果的顺序

  • 按订单
  • OrderByDescending
  • 逆转
  • 然后
  • 然后降序

根据一些规则重新定义订单。

  • GroupBy-根据产生每个IGrouping第一个键的源中元素的顺序,按顺序产生IGrouping对象。分组中的元素按它们在源中出现的顺序产生。
  • GroupJoin-GroupJoin保留外部元素的顺序,对于外部元素,从内部匹配元素的顺序。
  • 连接-保留外部元素的顺序,对于这些元素中的每个元素,保留内部匹配元素的顺序。
  • SelectMany-对于源的每个元素,都会调用选择器,并返回一个值序列。
  • 联合-枚举此方法返回的对象时,联合将按此顺序枚举第一和第二,并产生尚未产生的每个元素。

编辑:基于此实现,我已将Distinct移至Preserving order 。

    private static IEnumerable<TSource> DistinctIterator<TSource>
      (IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        Set<TSource> set = new Set<TSource>(comparer);
        foreach (TSource element in source)
            if (set.Add(element)) yield return element;
    }

2
实际上,我认为Distinct保留原始(首次发现)的顺序-因此{1,2,1,3,1,3,4,1,5}将是{1,2,3,4,5}
Marc Gravell

10
msdn.microsoft.com/zh-cn/library/bb348436.aspx Distinct <(Of <(TSource>)>)(IEnumerable <(Of <(TSource>)>))方法返回不包含重复值的无序序列。
艾米B

12
马克:您说的可能是正确的,但是依靠这种行为是一个坏主意。
艾米B

4
@Amy B是的,但不适用于Linq to Objects。在Linq to Sql中,distinct()将distinct关键字放入生成的sql中,并且不能保证从sql进行排序。我很想看到linq对不保留顺序的对象的实现,并且比保留顺序的对象更有效。例如,您可以使用整个输入并将其放入哈希集中,然后通过枚举哈希集(丢失顺序)来产生值,但这更糟。所以,是的,我不介意时不时地违反文档:)

4
也许文档(用于Distinct方法)只是要说“未排序”,而不是“按不可预测的顺序”。我会说Distinct属于上面的过滤类别Where
杰普·斯蒂格·尼尔森

34

您实际上是在谈论SQL还是数组?换句话说,您是使用LINQ to SQL还是LINQ to Objects?

LINQ to Objects运算符实际上并没有更改其原始数据源,而是建立了由数据源有效支持的序列。唯一会更改顺序的操作是OrderBy / OrderByDescending / ThenBy / ThenByDescending-即便如此,那些操作对于相同顺序的元素也是稳定的。当然,许多操作会过滤掉某些元素,但是返回的元素将以相同的顺序排列。

如果您转换为其他数据结构(例如,使用ToLookup或ToDictionary),则我不认为此时会保留顺序-但这还是有些不同。(我相信,映射到相同键的值的顺序会保留,以供查找。)


因此,由于OrderBy是稳定的排序,因此:seq.OrderBy(_ => _.Key)将元素与seq.GroupBy(_ => _.Key).SelectMany(_ => _ )。那是对的吗?
dmg 2016年

1
@dmg:不,不会。只是GroupBy随后SelectMany会给者皆分组的结果,而不是在按键的升序...这将使他们在最初发生的按键顺序。
乔恩·斯基特

您是说LINQ to SQL不保留顺序吗?
symbiont

@symbiont:在许多SQL操作有没有明确的顺序开始。基本上,我只是在对我可以保证的事情(例如LINQ to Objects)作出承诺。
乔恩·斯基特

@JonSkeet如果我使用OrderBy,它可以确保具有相同密钥的'n'个对象将保留其原始序列,除非它们在一起。即::list<x> {a b c d e f g}如果c,d,e都具有相同的键,则结果序列将包含c,d,e 并排 且顺序为c,d,e。我似乎找不到基于MS的绝对答案。
Paulustrious

7

如果您正在处理数组,听起来好像您使用的是LINQ-to-Objects,而不是SQL。你可否确认?大多数LINQ操作不会重新排序任何东西(输出将与输入顺序相同)-因此请勿应用其他排序方式(OrderBy [Descending] / ThenBy [Descending])。

[编辑:正如乔恩所说的那样;LINQ通常会创建一个序列,而将原始数据保留下来]

请注意,将数据推送到Dictionary<,>(ToDictionary)中会扰乱数据,因为字典不遵守任何特定的排序顺序。

但是最常见的事情(选择,哪里,跳过,取走)应该没问题。


如果我没有记错的话,那么就不对订单ToDictionary()做任何承诺,而是在实践中保持输入订单(直到您从其中删除某些内容为止)。我并不是说要依靠它,但是“加扰”似乎是不准确的。
Timo

4

我在引用官方文档的类似问题中找到了一个很好的答案。引用一下:

对于Enumerable方法(LINQ到对象,它适用于List<T>),你可以依赖通过返回的元素的顺序SelectWhereGroupBy。对于本质上无序的事物(例如ToDictionary或),情况并非如此Distinct

Enumerable.GroupBy文档中:

IGrouping<TKey, TElement>对象被产生在该基于产生的每个的所述第一密钥在源中的元素的顺序上的顺序IGrouping<TKey, TElement>。分组中的元素按它们出现的顺序产生source

对于IQueryable扩展方法(其他LINQ提供程序)不一定是正确的。

资料来源:LINQ的可数方法是否维持元素的相对顺序?


2

任何“分组依据”或“排序依据”都可能会更改顺序。


0

这里的问题专门针对LINQ-to-Objects。

如果您使用的是LINQ-to-SQL,则没有顺序,除非您强加以下命令:

mysqlresult.OrderBy(e=>e.SomeColumn)

如果您不使用LINQ-to-SQL执行此操作,则结果顺序在后续查询之间可能会有所不同,即使是相同的数据也可能会导致间歇性错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.