LINQ OrderBy 仅在第二个属性上

本文关键字:属性 第二个 OrderBy LINQ | 更新日期: 2023-09-27 18:33:56

>我有一个列表,比如

| column1 | column2 |
|    1    |   72    |
|    2    |   30    |
|    3    |   27    |
|    3    |   38    |
|    4    |   72    |

如您所见,该列表已经在第 1 列上排序,我在这里的目标是仅在等于第 1 列的第二列上执行 OrderByDescending。基本上,我想要

| column1 | column2 |
|    1    |   72    |
|    2    |   30    |
|    3    |   38    |
|    3    |   27    |
|    4    |   72    |

我无法重新运行第一个 OrderBy(这很难解释,我们不在乎,我只是不能:D),所以忘记
list.OrderBy(e => e.column1).ThenByDescending(e => e.column2)

事实上,如果我可以简单地做一个.ThenByDescending(e => e.column2)而不必做.OrderBy,我就不会有任何问题(也许我可以运行一个不会改变排序的"空"OrderBy?那我就可以做ThenByDescending了?

LINQ OrderBy 仅在第二个属性上

list.OrderBy(e => e.column1).ThenByDescending(e => e.column2)仍然是要走的路。

算法必须知道它必须首先对e.column1进行排序,无论它是否真的改变了某些东西。它必须知道它只需要在第一个排序语句的子集中对e.column2进行排序。你不能通过"只是"对column2进行排序来做到这一点。

list.GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))

将与许多提供商合作,但有些提供商可能无法保留GroupBy中的订单。在这种情况下:

list.AsEnumerable().GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))

将通过强制操作进入内存(其中顺序由 GroupBy 保留)来工作,尽管缺点是所有后续操作都在内存中而不是在数据库等上完成。

如果您确实有一个列表而不是 IEnumerable,实际上可以使用 List.Sort() 的重载来执行此操作,这使您可以指定要排序的项目子集以及比较器。

您要做的是对列表进行 O(N) 遍历,以根据已排序的列确定每个子组出现的位置。然后根据辅助排序列对多个项目的每个子组进行排序。

在选择用于标识子组的键

和用于对子组进行排序的键时,涉及少量繁琐的工作。

下面是实现:

public static void SortSubgroupsBy<T>
(
    List<T> items, 
    Func<T, T, bool> sortedColumnComparer,  // Used to compare the already-sorted column.
    Func<T, T, int>  unsortedColumnComparer // Used to compare the unsorted column.
)
{
    var unsortedComparer = Comparer<T>.Create(
        (x, y) => unsortedColumnComparer(x, y));
    for (int i = 0; i < items.Count; ++i)
    {
        int j = i + 1;
        while (j < items.Count && sortedColumnComparer(items[i], items[j]))
            ++j;
        if ((j - i) > 1)
            items.Sort(i, j-i, unsortedComparer);
    }
}

下面是控制台应用中的完整演示:

    using System;
    using System.Collections.Generic;
    namespace ConsoleApplication1
    {
        class Item
        {
            public Item(int column1, int column2)
            {
                Column1 = column1;
                Column2 = column2;
            }
            public int Column1;
            public int Column2;
            public override string ToString()
            {
                return $"[{Column1}, {Column2}]";
            }
        }
        class Program
        {
            static void Main()
            {
                List<Item> items = new List<Item>
                {
                    new Item(1, 72),
                    new Item(2, 29),
                    new Item(2, 30),
                    new Item(3, 27),
                    new Item(3, 38),
                    new Item(3, 53),
                    new Item(4, 72),
                    new Item(4, 21),
                    new Item(4, 86),
                    new Item(4, 17),
                    new Item(5, 90)
                };
                SortSubgroupsBy(
                    items, 
                    (x, y) => x.Column1 == y.Column1, // Compare sorted column.
                    (x, y) => y.Column2 - x.Column2); // Compare unsorted column.
                Console.WriteLine(string.Join("'n", items));
            }
            public static void SortSubgroupsBy<T>
            (
                List<T> items, 
                Func<T, T, bool> sortedColumnComparer,  // Used to compare the already-sorted column.
                Func<T, T, int>  unsortedColumnComparer // Used to compare the unsorted column.
            )
            {
                var unsortedComparer = Comparer<T>.Create(
                    (x, y) => unsortedColumnComparer(x, y));
                for (int i = 0; i < items.Count; ++i)
                {
                    int j = i + 1;
                    while (j < items.Count && sortedColumnComparer(items[i], items[j]))
                        ++j;
                    if ((j - i) > 1)
                        items.Sort(i, j-i, unsortedComparer);
                }
            }
        }
    }