【问题标题】:List unique transformation in LINQ列出 LINQ 中的唯一转换
【发布时间】:2013-11-27 14:54:36
【问题描述】:

我有一个有序的实体列表。每个实体都有一个int UniqueKey 属性。

我希望列表经过转换,从而使 UniqueKey 值唯一(假设有重复)。这是通过查找重复项并逐步增加它们来完成的。

分步流程:

  1. 从索引 1 开始 (我使用从零开始的索引)
  2. 如果任何先前的元素具有相同的UniqueId 值,则增加当前索引处的值。
  3. 重复 (2) 直到前面的元素都没有相同的 UniqueId
  4. 向右移动一个元素

例如,{ 1, 1, 1, 3, 3, 8 } 将执行以下步骤:

  1. { 1, 2, 1, 3, 3, 8 }:索引 1 递增
  2. { 1, 2, 2, 3, 3, 8 }:索引 2 递增
  3. { 1, 2, 3, 3, 3, 8 }:索引 2 再次递增
  4. { 1, 2, 3, 4, 3, 8 }:索引 3 递增
  5. { 1, 2, 3, 4, 4, 8 }:索引 4 递增
  6. { 1, 2, 3, 4, 5, 8 }:索引 4 再次递增

以下代码以非常程序化的方式执行上述算法:

entities = entities.OrderBy(x => x.UniqueId);

foreach (var entity in entities)
{
    var leftList = entities.Take(entities.IndexOf(entity));

    while (leftList.Any(x => x.UniqueId == entity.UniqueId))
    {
        entity.UniqueId++;
    }
}

问题:是否可以在 LINQ 中实现此功能?

【问题讨论】:

  • 为什么要在 LINQ 中实现这个?我认为你的算法在目前的情况下很清楚。您绝对可以加快它的速度(这会降低可读性),但使用 LINQ 更有可能使该算法变得更慢且可读性降低。 LINQ 不应该是你的金锤......
  • 您是否必须使用这种精确的算法,或者您是否可以使用任何算法来使所有 ID 都变得唯一?如果想使用 LINQ,有更好的方法为每个项目分配一个唯一的 ID。
  • shouldn't 将 LINQ 用于具有非查询语义的任务。只需使用命令式代码,它会更加清晰和简单。
  • 为什么要完成所有这些工作?为什么不把 {1, 1, 1, 2} 变成 {1, 2, 3, 4} 呢?

标签: c# .net algorithm linq functional-programming


【解决方案1】:

技术上是的:

var indexedEntities =
   entities.Select((e, i) => new { Entity = e, Index = i })
           .ToList();

indexedEntities.ForEach(ie =>
   ie.Entity.UniqueId =
      indexedEntities.Any(prev => prev.Index < ie.Index)
     && ie.Entity.UniqueId
        <= indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
                          .Max(prev => prev.Entity.UniqueId)
        ? indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
                         .Max(prev => prev.Entity.UniqueId) + 1
        : ie.Entity.UniqueId);

var result = indexedEntities.Select(ie => ie.Entity);

虽然拜托了,为了对 IT 中所有神圣事物的热爱,不要,只是不要 :)

【讨论】:

  • 你应该用TakeWhile替换你的Wheres
  • @Servy 你真的考虑过优化这个怪物吗?很公平,更新了:)
【解决方案2】:

这不会忠实地遵循您的算法,但它可能会给您想要的结果。 本质上将每个元素与下一个元素进行比较,并将后者的 Id 增加为比前者多一个。

entities.OrderBy(e => e.Id)
    .Aggregate((e1, e2) => { if (e1.Id >= e2.Id) { e2.Id = e1.Id + 1; } return e2; });

【讨论】:

  • 这什么都不做 - 你首先做一个 OrderBy 然后只有当前一个元素大于下一个元素时才做任何事情。您的意思是 >= 而不是 >?
【解决方案3】:

你的算法可以简化很多。只需迭代一遍,如果一个 Id 低于前一个,则将其加一。没有 Linq,没有 O(n^2),只有 O(n):

{ 1, 2, 1, 3, 3, 8 } : Index 1 incremented
{ 1, 2, 3, 3, 3, 8 } : Index 2 incremented
{ 1, 2, 3, 4, 3, 8 } : Index 3 incremented
{ 1, 2, 3, 4, 5, 8 } : Index 4 incremented

entities = entities.OrderBy(x => x.UniqueId).ToList();
for(int index = 1; index < entities.Count; index++)
{
    int previous = entities[index - 1].UniqueId;
    if (previous >= entities[index].UniqueId)
    {
        entities[index].UniqueId = previous + 1;
    }
}

【讨论】:

    【解决方案4】:

    如果您迫切需要 linq 解决方案,为什么不直接使用索引作为 id。

    entities.OrderBy(x => x.UniqueId).Select((x,i) => {
                                                         x.UniqueId = i;
                                                         return x;
                                                      }).ToArray();           
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-07
      • 2021-06-22
      • 2013-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      • 1970-01-01
      相关资源
      最近更新 更多