如何对嵌套属性进行分组并在 LINQ 中具有相同的父级?

发布于 2024-11-05 08:44:34 字数 1142 浏览 0 评论 0原文

我是 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;,我想获取所有 Simfiles 中包含的所有 Notecharts 的列表,并按以下条件分组(按优先顺序排列):
1) Notechart. 的值,其中 是任意 Notechart 特定字段或 Notechart 特定字段的转换(例如任何 BPM 介于 130-150 之间)
2)位于同一个 Simfile 中(因为如果 simfile 中的两个 notecharts 具有与上述相同的条件,我希望将它们与 simfile 中的信息一起显示)

是否可以在 <代码>LINQ? MyElementMyElementCollection 没有实现任何自定义相等性检查,因此我不相信测试它是否在列表中会起作用。谢谢!

I'm new to LINQ, and I'm having trouble organizing this query to return what I want. First off, some background. I'm working on a music game, which supports custom notecharts. Notecharts contain metadata specific to a collection of notes, such as number of notes, difficulty, and BPM. One or more notecharts can be in a simfile, sort of a container of notecharts. A simfile has its own "simfile-level" metadata as well, such as a path to a song file, song title, song artist, etc. So, I have classes of the following form:

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...
}

For a given List<Simfile>, I'd like to get a list of all Notecharts contained in all the Simfiles grouped by the following criteria (in order of precedence):
1) The value of Notechart.<Value>, where <Value> is any of the notechart-specific fields or a transformation of a notechart-specific field (e.g. any BPMs between 130-150)
2) Being in the same Simfile (since if two notecharts within a simfile have the same criteria as above, I would want to display them together with information from the simfile)

Is there anyway to represent this in LINQ? MyElement and MyElementCollection don't implement any custom equality checking, so I don't believe testing if it is in the list will work. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

荆棘i 2024-11-12 08:44:34

您可以传递匿名类型作为分组键以对多个值进行分组:

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);

You can pass an anonymous type as the grouping key to group over multiple values:

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);
删除会话 2024-11-12 08:44:34

我认为您缺少的是 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
                                      })
               });

I think what you're missing is the SelectMany extension method. Try this:

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
                                      })
               });
耳钉梦 2024-11-12 08:44:34

现在使用更完整的示例(特别是我熟悉的东西)可以更轻松地可视化您的问题。最终我的查询大部分保持不变。为了获得您想要的分组类型,您应该定义一个辅助类来对 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)
            };

It's a lot easier to visualize your problem now with a more complete example (particularly with something I'm familiar with). Ultimately the query I had mostly stays the same. To get the kind of grouping you want, you should define a helper class to classify BPM ranges.

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)
            };
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文