使用LINQ保持顺序

mhd8tkvw  于 2023-03-10  发布在  其他
关注(0)|答案(7)|浏览(437)

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

ia2d9nvy

ia2d9nvy1#

我检查了System.Linq.Enumerable的方法,丢弃了所有返回非IEnumerable结果的方法,并检查了每个方法的注解,以确定结果的顺序与源代码的顺序有何不同。
绝对保持顺序。可以按索引将源元素Map到结果元素

  • 作为可枚举的
  • 铸型
  • 孔卡特
  • 选择
  • 目标阵列
  • 任务列表

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

  • 独特
  • 除了
  • 相交
  • 类型
  • 预置(.net 4.7.1中的新功能)
  • 跳过
  • 跳过时间
  • 休息一会儿
  • 地点
  • Zip(.net 4中的新功能)

破坏秩序--我们不知道预期的结果是什么。

  • 到字典
  • 目标查找

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

  • 订购者
  • 按降序排序
  • 反转
  • 然后
  • 然后按降序

根据某些规则重新定义Order。

  • GroupBy -IGrouping对象的生成顺序基于源中生成每个IGrouping的第一个键的元素的顺序。分组中的元素按它们在源中出现的顺序生成。
  • GroupJoin - GroupJoin保留outer元素的顺序,对于outer的每个元素,保留与inner匹配的元素的顺序。
  • Join -保留outer元素的顺序,对于每个元素,保留inner匹配元素的顺序。
  • SelectMany -对于source的每个元素,调用选择器并返回一个值序列。
  • 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;
    }
py49o6xq

py49o6xq2#

你实际上是在谈论SQL,还是数组?换句话说,你是在使用LINQ to SQL还是LINQ to Objects?
LINQ to Objects操作符实际上并不改变它们的原始数据源--它们构建由数据源有效支持的序列。唯一改变顺序的操作是OrderBy/OrderByDescending/ThenBy/ThenByDescending --即使这样,这些操作对于相同顺序的元素也是稳定的。当然,许多操作会过滤掉一些元素,但返回的元素将处于相同的顺序。
如果你转换成一个不同的数据结构,例如ToLookup或ToDictionary,我不认为在这一点上顺序是保持的--但无论如何这是有些不同的(我相信Map到同一个键的值的顺序是为了查找而保持的)。

pw9qyyiw

pw9qyyiw3#

如果您正在处理一个数组,听起来像是在使用LINQ-to-Objects,而不是SQL;大多数LINQ操作不会重新排序任何东西(输出的顺序与输入的顺序相同)-所以不要应用其他排序(OrderBy[Descending]/ThenBy[Descending])。
[edit:正如乔恩说得更清楚;LINQ通常会创建一个 * 新 * 序列,而不处理原始数据]
请注意,将数据推入Dictionary<,>(ToDictionary)将打乱数据,因为Dictionary不考虑任何特定的排序顺序。
但最常见的事情(选择,在哪里,跳过,采取)应该是好的。

eyh26e7m

eyh26e7m4#

我在一个类似的问题中找到了一个很好的答案,这个问题引用了官方文件。
对于Enumerable方法(LINQ to Objects,适用于List<T>),您可以依赖SelectWhereGroupBy返回的元素的顺序,但对于ToDictionaryDistinct等本质上无序的对象,情况并非如此。

来自Enumerable.GroupBy文档:

IGrouping<TKey, TElement>对象的生成顺序基于源中生成每个IGrouping<TKey, TElement>的第一个键的元素的顺序。分组中的元素按照它们在source中出现的顺序生成。
对于IQueryable扩展方法(其他LINQ提供程序)不一定如此。
源代码:LINQ的可枚举方法是否保持元素的相对顺序?

vaqhlq81

vaqhlq815#

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

but5z9lq

but5z9lq6#

这里的问题特指LINQ-to-Objects。
如果您使用LINQ-to-SQL,则没有顺序,除非您使用以下内容强制执行:

mysqlresult.OrderBy(e=>e.SomeColumn)

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

des4xlb0

des4xlb07#

对我来说,问题是确定默认的排序顺序,结果是按2列排序,如下所示。经过多次迭代,我能够找到默认的排序顺序,并在我的LINQ查询中重做它。为了删除重复项,使用一个简单的foreach来创建一个没有重复项的新字符串列表。

//original sorting order lost
var inv2 = db.Inventories
.GroupBy(l => l.VendorFullSKU)
.Select(cl => new Inventory2
{
    VariantID = cl.FirstOrDefault() == null ? 0 : cl.FirstOrDefault().VariantID,
    Quan = cl.Sum(c => c.Quan), 
    Color = cl.FirstOrDefault() == null ? "" : cl.FirstOrDefault().Color    
});

//original sorting order restored
var bl = (from pv in db.ProductVariants
join inv in inv2 on pv.VariantID equals inv.VariantID
orderby inv.VariantID, inv.Color //sort
select inv.Color
).ToList();

//remove duplicates while preserving original sort order
var colorsDistinct = new List<string>();
foreach (var item in bl)
{
    if (!colorsDistinct.Contains(item))
        colorsDistinct.Add(item);
}

相关问题