【问题标题】:Preserving order with LINQ使用 LINQ 保持顺序
【发布时间】:2010-09-17 07:09:43
【问题描述】:

我在有序数组上使用 LINQ to Objects 指令。 我不应该做哪些操作来确保数组的顺序没有改变?

【问题讨论】:

    标签: c# arrays linq sorting data-structures


    【解决方案1】:

    我检查了 System.Linq.Enumerable 的方法,丢弃了任何返回非 IEnumerable 结果的方法。我检查了每个人的备注,以确定结果的顺序与来源的顺序有何不同。

    绝对保持秩序。您可以通过索引将源元素映射到结果元素

    • AsEnumerable
    • 演员表
    • 连接
    • 选择
    • ToArray
    • ToList

    保持秩序。元素被过滤或添加,但不会重新排序。

    • 不同
    • 除了
    • 相交
    • 类型
    • 前置(.net 4.7.1 中的新功能)
    • 跳过
    • 跳过时
    • 拍摄
    • TakeWhile
    • 在哪里
    • Zip(.net 4 中的新功能)

    Destroys Order - 我们不知道预期的结果是什么顺序。

    • 到字典
    • ToLookup

    显式重新定义顺序 - 使用这些来更改结果的顺序

    • 订购者
    • 降序排列
    • 反向
    • 然后通过
    • 然后按降序

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

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

    编辑:我已根据 implementation 将 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;
        }
    

    【讨论】:

    • 实际上,我认为 Distinct 保留了原始(首次找到)顺序 - 所以 {1,2,1,3,1,3,4,1,5} 将是 {1,2,3, 4,5}
    • msdn.microsoft.com/en-us/library/bb348436.aspx Distinct)>)(IEnumerable)>)) 方法返回一个不包含重复值的无序序列。
    • Marc:你说的可能是真的,但依赖这种行为是个坏主意。
    • @Amy B 是的,但它不适用于 Linq to Objects。在Linq to Sql中,distinct()将distinct关键字放入生成的sql中,不保证从sql排序。我很想看到一个不同的实现 linq 到不保留顺序的对象,并且比保留顺序的对象更有效。例如,您可以使用整个输入并将其放入哈希集中,然后通过枚举哈希集(丢失顺序)来产生值,但这更糟。所以,是的,我不介意时不时地违反文档:)
    • 也许文档(用于Distinct 方法)只是说“未排序”,而不是“以不可预测的顺序”。我想说Distinct 属于上面的过滤类别,就像Where 一样。
    【解决方案2】:

    这里的问题特指LINQ-to-Objects。

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

    mysqlresult.OrderBy(e=>e.SomeColumn)
    

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

    【讨论】:

      【解决方案3】:

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

      对于Enumerable 方法(LINQ to Objects,适用于List&lt;T&gt;),您可以依赖SelectWhereGroupBy 返回的元素顺序。对于像 ToDictionaryDistinct 这样本质上无序的事物,情况并非如此。

      来自Enumerable.GroupBy 文档:

      IGrouping&lt;TKey, TElement&gt; 对象的生成顺序基于 source 中生成每个 IGrouping&lt;TKey, TElement&gt; 的第一个键的元素的顺序。分组中的元素按照它们在source 中出现的顺序产生。

      IQueryable 扩展方法(其他 LINQ 提供程序)不一定如此。

      来源:Do LINQ's Enumerable Methods Maintain Relative Order of Elements?

      【讨论】:

        【解决方案4】:

        任何 'group by' 或 'order by' 都可能会改变顺序。

        【讨论】:

          【解决方案5】:

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

          [编辑:正如乔恩说得更清楚; LINQ 一般会创建一个新的序列,不理会原始数据]

          请注意,将数据推送到 Dictionary&lt;,&gt; (ToDictionary) 会打乱数据,因为字典不遵循任何特定的排序顺序。

          但最常见的事情(Select、Where、Skip、Take)应该没问题。

          【讨论】:

          • 如果我没记错的话,ToDictionary() 只是对订单没有承诺,但实际上会维护输入顺序(直到您从中删除某些内容)。我不是说要依赖这个,但“加扰”似乎不准确。
          【解决方案6】:

          您实际上是在谈论 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:不,不会。只需 GroupBy 后跟 SelectMany 将给出按键分组的结果,但不是按键升序排列...它将按键最初出现的顺序给出。
          • 你是说 LINQ to SQL 不保存顺序?
          • @symbiont:在许多 SQL 操作中,没有开始时没有明确定义的顺序。基本上,我试图只对我可以保证的事情做出承诺 - 例如 LINQ to Objects。
          • @Paulustrious:在 LINQ to Objects 中,是的。在其他提供程序中,它是特定于实现的。
          猜你喜欢
          • 2012-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-29
          相关资源
          最近更新 更多