LINQ, SelectMany具有多个可能的结果

4

我有这样一种情况,需要合并对象列表。列表中的每个对象都有一个属性,用于说明在合并器中应如何处理它。因此,请假设以下情况...

enum Cascade {
  Full,
  Unique,
  Right,
  Left
}

class Note {
  int Id { get; set; }
  Cascade Cascade { get; set; }
  // lots of other data.
}

var list1 = new List<Note>{
 new Note {
   Id = 1,
   Cascade.Full,
   // data
 },
 new Note {
   Id = 2,
   Cascade.Right,
   // data
 }
};
var list2 = new List<Note>{
  new Note {
   Id = 1,
   Cascade.Left,
   // data
  }
};
var list3 = new List<Note>{
  new Note {
    Id = 1,
    Cascade.Unique,
    // data similar to list1.Note[0]
  }
}

那么,我将有一个方法...
Composite(this IList<IList<Note>> notes){
  return new List<Note> {
      notes.SelectMany(g => g).Where(g => g.Cascade == Cascade.All).ToList()
      // Here is the problem... 
      .SelectMany(g => g).Where(g => g.Cascade == Cascade.Right)
      .Select( // I want to do a _LastOrDefault_ )
      // continuing for the other cascades. 
  }
}

这是我迷失的地方。我需要执行多个SelectMany语句,但我不知道该如何做。但这是预期行为

Cascade.Full

Note将始终出现在最终集合中。

Cascade.Unique

Note将只出现一次在最终集合中,忽略任何重复项。

Cascade.Left

Note将出现在最终集合中,首个实例优先于后续实例。(因此,注释1、2、3是相同的。注释1被推送)

Cascade.Right

Note将出现在最终集合中,最后一个实例优先于重复项。(因此,注释1、2、3是相同的。注释3被推送)


1
我发现当使用LINQ方式不是很明显时,编写“长”代码通常很有帮助。这样做可以让你筛选所有细节并获得一个可行的解决方案。然后,使用LINQ版本变得更容易理解。 - Anthony Pegram
需要保留笔记的顺序吗?还是只要最终列表,无论顺序如何? - Benjamin Podszun
顺序并不重要,只要在最终编译中存在正确的注释即可。 - Ciel
你确定没有奇怪的边缘情况吗?比如 1,Unique / 1,Right(哪个赢了?反过来也一样,1,Left / 1,Unique?1,Left / 1,Right 呢?)。 - Benjamin Podszun
1个回答

3
我认为你应该将问题分解成较小的部分。例如,您可以在单独的扩展方法中实现级联规则以针对个别列表。以下是我未经测试的解决方法:
public static IEnumerable<Note> ApplyCascades(this IEnumerable<Note> notes)
    {
        var uniques = new HashSet<Note>();
        Note rightToYield = null;
        foreach (var n in notes)
        {
            bool leftYielded = false;

            if (n.Cascade == Cascade.All) yield return n;
            if (n.Cascade == Cascade.Left && !leftYielded)
            {
                yield return n;
                leftYielded = true;
            }
            if (n.Cascade == Cascade.Right)
            {
                rightToYield = n;
            }
            if (n.Cascade == Cascade.Unique && !uniques.Contains(n))
            {
                yield return n;
                uniques.Add(n);
            } 
        }

        if (rightToYield != null) yield return rightToYield;
    } 
}

这种方法可以实现原始扩展方法,例如:
    List<Note> Composite(IList<IList<Note>> notes)
    {
        var result = from list in notes
                     from note in list.ApplyCascades()
                     select note;
        return result.ToList();

    }

这比使用linq语句解决问题要好得多。但我认为你需要使用if (n.Cascade == Cascade.Unique && !uniques.Add(n))) - Magnus
@Magnus 谢谢,已修复(需要将注释添加到 HashSet 中)。 - jeroenh
谢谢,这个工作还不错。但我很失望它不能在单个LINQ查询中完成。 - Ciel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接