如何在嵌套属性上进行分组以及在 LINQ 中具有相同的父属性

本文关键字:属性 LINQ 嵌套 | 更新日期: 2023-09-27 17:55:35

我是LINQ新手,我在组织此查询以返回我想要的内容时遇到问题。首先,一些背景。我正在开发一款支持自定义笔记图表的音乐游戏。注释图表包含特定于注释集合的元数据,例如注释数、难度和 BPM。一个或多个笔记图表可以位于一个 simfile 中,类似于笔记图表的容器。simfile 也有自己的"simfile 级"元数据,例如歌曲文件的路径、歌曲标题、歌曲艺术家等。因此,我有以下形式的类:

class Notechart {
    public List<Note> Notes { get; }
    public uint BPM { get; set; }
    public uint Difficulty { get; set; }
}
class Simfile {
    public List<Notechart> Notecharts { get; }
    public string SongPath { get; set; }
    public string SongTitle { get; set; }
    public string SongArtist { get; set; }
    // Other "album-specific" fields...
}

对于给定的List<Simfile>,我想获得按以下条件(按优先级顺序)分组的所有Simfiles中包含的所有Notecharts的列表:
1) Notechart.<Value> 的值,其中<Value>是任何特定于注释图表的字段或特定于注释图表的字段的转换(例如,介于 130-150 之间的任何 BPM)
2)处于同一Simfile(因为如果一个simfile中的两个笔记图表具有与上述相同的标准,我希望将它们与simfile中的信息一起显示)

无论如何可以在LINQ中表示这一点吗? MyElementMyElementCollection没有实现任何自定义相等性检查,所以我认为测试它是否在列表中会起作用。谢谢!

如何在嵌套属性上进行分组以及在 LINQ 中具有相同的父属性

可以将匿名类型作为分组键传递到多个值上进行分组:

void Sample(INumerable<SimeFile> simeFiles, Func<NoteChart, bool> noteChartPredicate)
{
  var xyz =
    from sf in fimFiles
    from nc in sf.NoteCharts
    group nc by new{Matched=noteChartPredicate(nc), SimFile=sf} into g
    select new{g.Key.SimFile, g.Key.Matched, NoteCharts = g.ToList()};
  ...
}
Sample(simFiles, nc => nc.BPM >= 130 && nc.BPM <= 150);

我认为您缺少的是SelectMany扩展方法。试试这个:

List<MyElementCollection> elementCollections = ...;
var grouped = (from ec in elementCollections
               select new
               {
                   ElementCollection = ec,
                   GroupedElements = (from e in ec.Elements
                                      group e by e.Value into g
                                      select new
                                      {
                                          Value = g.Key,
                                          Elements = g
                                      })
               });

现在用一个更完整的示例(特别是我熟悉的东西)来可视化你的问题要容易得多。 最终,我的查询大多保持不变。 要获得所需的分组类型,您应该定义一个帮助程序类来对 BPM 范围进行分类。

public class BpmRange
{
    static uint[] thresholds = new uint[] { 0, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, uint.MaxValue };
    public static BpmRange GetRange(uint bpm)
    {
        var index = Enumerable.Range(1, thresholds.Length - 1).First(i => bpm < thresholds[i]);
        return new BpmRange(thresholds[index - 1], thresholds[index]);
    }
    private BpmRange(uint lowerInclusive, uint upperExclusive) { range = Tuple.Create(lowerInclusive, upperExclusive); }
    private Tuple<uint, uint> range;
    public uint LowerInclusive { get { return range.Item1; } }
    public uint UpperExclusive { get { return range.Item2; } }
    public override bool Equals(object obj) { var asRange = obj as BpmRange; return asRange != null && this.range.Equals(asRange.range); }
    public override int GetHashCode() { return range.GetHashCode(); }
    public override string ToString() { return String.Format("[{0}, {1})", LowerInclusive, UpperExclusive); }
}
List<Simfile> simfiles = ...;
var query = from sim in simfiles
            select new
            {
                Simfile = sim,
                ByRange = from chart in sim.Notecharts
                          group chart by BpmRange.GetRange(chart.BPM)
            };