我是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
中表示这一点吗? MyElement
和MyElementCollection
没有实现任何自定义相等性检查,所以我认为测试它是否在列表中会起作用。谢谢!
可以将匿名类型作为分组键传递到多个值上进行分组:
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)
};