C#如何用LINQ Group By按双键分组嵌套Fragment集合
LINQ实现通话片段聚合方案
核心通过双字段分组完成同通话片段的挂载,完整链路直接覆盖从原始IEnumerable<CallImporterRow>输入到最终带同组片段集合的CallKeys对象输出的全流程。
实现代码
IEnumerable<CallKeys> GetAggregatedCalls(IEnumerable<CallImporterRow> CallImporterData) { return CallImporterData // 1. 原始行到CallKeys对象的初步映射,和现有映射逻辑对齐 .Select(row => { var userDefParts = row.CallUserDef.Split('_', StringSplitOptions.RemoveEmptyEntries); return new CallKeys { CallKey = int.Parse(userDefParts[0]), CallKeyDay = int.Parse(userDefParts[1]), StorageId = row.FragmentStorageId, SubDirectory = row.FragmentSubDirectory, Filename = row.AudioFileName, // 其余Fragment基类属性按业务规则映射即可 }; }) // 2. 按通话唯一标识双字段分组 .GroupBy(item => new { item.CallKey, item.CallKeyDay }) // 3. 组装单通通话的元对象,挂载所有所属片段 .Select(callGroup => { // 取分组内第一条记录作为整通通话的元信息载体 CallKeys currentCallMeta = callGroup.First(); // 同组所有片段转为Fragment集合,赋值给元对象的片段集合属性 currentCallMeta.Rows = callGroup.Cast<Fragment>(); // 可选:按片段顺序给Section字段赋值,标记片段在整通录音中的序号 int sectionNum = 1; foreach (var fragment in currentCallMeta.Rows) { fragment.Section = sectionNum++; } // 可选:在这里直接给TargetFilename赋值,设置合并后整通音频的存储文件名 // currentCallMeta.TargetFilename = $"call_{currentCallMeta.CallKeyDay}_{currentCallMeta.CallKey}.wav"; return currentCallMeta; }); }
关键说明
- 分组时使用匿名类型作为分组键,依赖匿名类型的默认值相等判断逻辑,只要
CallKey和CallKeyDay值一致就会被归为同一通通话,不需要额外实现等值比较器 - 如果需要保证片段顺序和实际通话顺序一致,可以在分组前增加
OrderBy逻辑,按片段的起始时间、文件名序号等字段排序,避免后续合并音频时顺序错乱 - 如果你已经提前完成了初步映射生成了
List<CallKeys>,可以跳过第一步的Select映射,直接对已有集合做GroupBy和后续组装即可 Rows属性如果定义为List<Fragment>类型,只需要在赋值的时候追加.ToList()即可,和IEnumerable<Fragment>类型兼容
内容的提问来源于stack exchange,提问作者TryHard
相关产品推荐
相关产品推荐

