【问题标题】:How to order by C# list so that the duplicate items appear in the reverse order of their insertion如何按 C# 列表排序,以便重复项以插入的相反顺序显示
【发布时间】:2018-05-21 22:02:30
【问题描述】:

如何按列表排序,以便重复项以插入的相反顺序出现(考虑到我不会使用列表的插入方法在中间插入任何项目)

举个例子:

public class Program
{
    public static void Main(string[] args)
    {
        List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

        list.Add(new KeyValuePair<int, string>(1, "A"));
        list.Add(new KeyValuePair<int, string>(3, "F"));
        list.Add(new KeyValuePair<int, string>(4, "G"));
        list.Add(new KeyValuePair<int, string>(2, "B"));
        list.Add(new KeyValuePair<int, string>(2, "C"));
        list.Add(new KeyValuePair<int, string>(3, "E"));
        list.Add(new KeyValuePair<int, string>(3, "D"));

        list=list.OrderBy(a=>a.Key).ToList();

        foreach(var item in list)
        {
            Console.WriteLine(item);
        }
    }
}

输出:

[1, A]
[2, B]
[2, C]
[3, F]
[3, E]
[3, D]
[4, G]

我想要以插入相反顺序的重复顺序,例如。 G。 F、E、D 应显示为 D、E、F。

我虽然 orderByDescending 可以解决问题,但也可以保持插入顺序。

【问题讨论】:

  • 您是在钥匙上订购,钥匙是相同的,因此不会改变这些物品的原始顺序。
  • 你的相反观点是什么?自然反转或您的列表顺序
  • 插入顺序是什么?名单?
  • 你确定这个要求,因为它有一个非常具体的含义:这样重复的项目就会以它们插入的相反顺序出现。
  • @Randeep 您打算如何缓解列表中间可以插入项目的问题?

标签: c# .net list linq


【解决方案1】:

OrderBy 是一种稳定的排序,这意味着无法区分的 Items 保持相同的顺序。 https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby?view=netframework-4.7.2

这意味着重复的项目将按照它们添加的顺序保留,因此我们可以这样做

list = list
        .GroupBy(kvp => kvp.Key)
        .OrderBy(kvp => kvp.Key)
        .SelectMany(g => g.Reverse())
        .ToList();

所以我们按键分组,组中的唯一项将有 1 项,重复项将超过 1 项。组中的项目将与源 Enumerable 的顺序相同,请参阅here

所以我们可以按组的键进行排序,并且对于每个组,我们以相反的顺序选择其项目。

这也有效

list = list
        .Select((x, i) => new { Item = x, Index = i })
        .OrderBy(_ => _.Item.Key)
        .ThenByDescending(_ => _.Index)
        .Select(_ => _.Item)
        .ToList();

这利用了Select Override,它为您提供集合中项目的索引以及项目

【讨论】:

  • 这将起作用,前提是没有人将值插入到列表中所选索引处而不是末尾处。然后索引,因此列表中的当前顺序将无法准确反映插入顺序。但我喜欢这段代码:)
  • 这个和@user7396598有同样的缺点。还做一个GroupBy 而不对每个组做任何事情是hacky。
  • 在 hacky 和使用工具集之间有一条细线,那个版本拖着这条线,我会给你。我添加了第二个版本
  • @Dave 是的,我知道。这就是诀窍。对你有好处。
  • @Dave 谢谢!正是我想要的。
【解决方案2】:

我还没有仔细检查语法,但是...

list
    .Zip(Enumerable.Range(0, list.Count), (item, insertOrder) => new { Item = item, InsertOrder = insertOrder })
    .OrderBy(x => x.Item.Key)
    .ThenBy(x => x.InsertOrder)
    .Select(x => x.Item)
    .ToList();

这样做是创建一个Enumerable.Range 来记录每个项目的插入顺序,然后是OrderByThenBy 正确排序项目的简单案例,然后Select 删除InsertOrder再次。

正如 user7396598 非常正确地指出的那样,列表的IEnumerable 顺序不一定是它们被插入的顺序。但是,如果您在没有使用特定索引的情况下插入项目的已知上下文中使用它,那么它就足够了。如果上下文未知,您需要扩展您正在捕获的数据以包含插入顺序,可能通过创建您自己的记录插入顺序且不允许索引插入的集合类。

希望对你有帮助

【讨论】:

  • 聪明,可能是唯一能满足问题要求的解决方案。
  • 并非如此。这会根据列表中每个条目的当前索引为您提供插入顺序。 List 提供了一个 Insert(index) 方法,会使这些结果不正确。
  • @user7396598 是的,但您正在创建 OP 从未要求的其他场景。
  • @CodingYoshi 这不是要求的问题,而是 List 的行为破坏了他所使用的逻辑的预期结果。
【解决方案3】:

试试这个:

list=list.OrderBy(a => a.Key).ThenBy(a => a.Value).ToList();

我相信这会给你想要的结果。但不是你解释的逻辑执行。

为了通过插入来订购它们,您需要以某种方式跟踪该信息。在您当前的代码中,您的订单对何时将项目添加到列表一无所知,它只知道键和值。

您可以使用自定义对象而不是键值对来执行此操作。

组成的对象
List<kvp<int, kvp<int, string>>> 

使用它自己的 Add() 方法就可以了。使用外部 int 作为插入顺序。使用对 Add() 的覆盖来获取列表的当前长度并在添加新对象之前增加插入顺序,从而保持正确的顺序,而不管插入的索引是什么。

【讨论】:

  • 重复项以插入的相反顺序出现,而不是值的相反顺序。你是按照价值观去做的。
  • @CodingYoshi 不开玩笑,这就是解释所说的...
  • 是的,但您没有回答问题,您是在说“这是按价值排序,但不是您想要的 OP”。所以你没有回答这个问题。
  • @CodingYoshi 我也提供了问题的答案...他必须知道插入顺序并跟踪它
  • 好吧,人们似乎对你的答案投了反对票,所以你可能想对此做点什么。我什至还没有投票...
猜你喜欢
  • 2017-10-09
  • 2012-10-30
  • 2018-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多