如何用一步高效LINQ操作筛选对应GPA达标的Grades列表元素?
问题描述
我有两个长度保证一致的double类型列表Grades和GPA,想要创建第三个double类型列表PassingGrades,仅包含Grades中那些索引与GPA中通过测试的元素索引对应的元素。
初始代码如下:
List<double> Grades = new List<double>() { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; List<double> GPA = new List<double>() { 0.4, 0.8, 1.2, 1.6, 2.0, 2.4, 2.8, 3.2, 3.6, 4.0 }; if (Grades.Count != GPA.Count) { return; } double threshold = 3.0;
我目前的两种实现方式:
- 常规for循环写法:
List<double> PassingGrades = new List<double>(); for (int i = 0; i < GPA.Count; i++) { if (GPA[i] >= threshold) { PassingGrades.Add(Grades[i]); } }
- 分两步筛选写法:
var indices = GPA.Select((b, i) => b >= threshold ? i : -1).Where(i => i != -1).ToArray(); List<double> PassingGrades = Grades.Where((g, i) => indices.Contains(i)).ToList();
请问是否存在一种一步实现且效率不低的写法?
解决方案
当然有,你可以使用LINQ的Zip方法实现一步到位的高效筛选。Zip方法会将两个序列中索引对应的元素配对,之后只需筛选出符合GPA阈值条件的配对,再提取对应的Grades元素即可。
这种写法仅需遍历两个序列一次,时间复杂度为O(n),和你写的for循环效率相当,但代码更简洁。
实现代码如下:
List<double> PassingGrades = Grades.Zip(GPA, (grade, gpa) => new { grade, gpa }) .Where(pair => pair.gpa >= threshold) .Select(pair => pair.grade) .ToList();
如果不想创建匿名类型,也可以直接在配对逻辑中返回符合条件的grade,写法更紧凑:
List<double> PassingGrades = Grades.Zip(GPA, (grade, gpa) => gpa >= threshold ? grade : (double?)null) .Where(grade => grade.HasValue) .Select(grade => grade.Value) .ToList();
对比你之前的两步写法,那种方式里indices.Contains(i)每次检查都是O(n)操作,整体时间复杂度会变成O(n²),当列表元素较多时效率会明显下降。而Zip的写法既保持了一步实现的简洁性,又保证了和for循环一致的高效性。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

