我在面试过程中有这个问题,有以下列表
Tom 1000
Mark 2200
Antony 3000
Paul 2500
Kris 2800
Ron 3110
使用 group by 编写 linq 查询以获取中间有数字的人群
0-1500
1501-2500
2501-4000
怎么做到呢 ?
int?[] ranges = new int?[] { 1500, 2500, 4000 };
var groups = from p in people
group p by ranges.FirstOrDefault(r => r > p.Value) into g
where g.Key != null
select new {
People = g,
To = g.Key,
From = ranges.Where(r => r < g.Key)
.Select(r => r + 1).DefaultIfEmpty(0).Last()
};
样本数据:
var people = new List<Person>
{
new Person { Name = "Tom", Value = 1000 },
new Person { Name = "Mark", Value = 2200 },
new Person { Name = "Antony", Value = 3000 },
new Person { Name = "Paul", Value = 2500 },
new Person { Name = "Kris", Value = 2800 },
new Person { Name = "Kris", Value = 5800 },
};
输出:
0-1500
1000: Tom
1501-2500
2200: Mark
2501-4000
3000: Antony
2500: Paul
2800: Kris
当然,如果你想让你的代码看起来更好,你可以创建一些 Range 类。例如,以下查询将返回适合范围内的人的范围:
var ranges = new List<Range<int>> { 0.To(1500), 1501.To(2500), 2501.To(4000) };
var groups = from r in ranges
select new {
Range = r,
People = people.Where(p => r.Contains(p.Value))
};
通用范围类:
public class Range<T>
where T : IComparable
{
public Range(T from, T to)
{
if (from.CompareTo(to) > 0)
throw new ArgumentException("From should not be greater than To");
From = from;
To = to;
}
public T From { get; private set; }
public T To { get; private set; }
public bool Contains(T value)
{
return value.CompareTo(From) >= 0 && value.CompareTo(To) <= 0;
}
}
以及允许编写代码的扩展,例如0.To(100)
从任何可比较的类型创建范围:
public static Range<T> To<T>(this T from, T to)
where T: IComparable
{
return new Range<T>(from, to);
}
您可以GroupBy
使用List.FindIndex
:
var ranges = new[]{
new{ Start=0, End=1500 }, new{ Start=1501, End=2500}, new{ Start=2501, End=4000}
}.ToList();
var players = new[]{
new{ Name = "Tom", Score = 1000 },
new{ Name = "Mark", Score = 2200 },
new{ Name = "Antony", Score = 3000 },
new{ Name = "Paul", Score = 2500 },
new{ Name = "Kris", Score = 2800 },
new{ Name = "Ron", Score = 3110 },
};
var scoreGroups = players.
GroupBy(p => ranges.FindIndex(r => p.Score >= r.Start && p.Score <= r.End));
foreach (var scoreGroup in scoreGroups)
Console.WriteLine("Range: {0} <--> {1} Players: {2}"
, ranges[scoreGroup.Key].Start
, ranges[scoreGroup.Key].End
, string.Join(", ", scoreGroup.Select(p => p.Name));
结果:
Range: 0 <--> 1500 Players: Tom
Range: 1501 <--> 2500 Players: Mark, Paul
Range: 2501 <--> 4000 Players: Antony, Kris, Ron
list.GroupBy(g => PickGroup(g));
private string PickGroup(int val)
{
//some logic to determine which group the input falls into
//Check the value against the boundaries of the groups
}
试试上面的方法
根据您的范围定义组并返回表示范围(0-1500)等的字符串
这是另一种方式:
var people = new List<Tuple<int, string>> { new Tuple<int,string>(1000, "Tom"),
new Tuple<int,string>(2200, "Mark"),
new Tuple<int,string>(3000, "Antony"),
new Tuple<int,string>(2500, "Paul"),
new Tuple<int,string>(2800, "Kris"),
new Tuple<int,string>(3110, "Ron"),
};
var ranges = new List<Tuple<int, int>> { new Tuple<int,int>(0,1500),
new Tuple<int,int>(1501,2500),
new Tuple<int,int>(2501,4000),
};
var result = people.GroupBy(person => ranges.FirstOrDefault(e => e.Item1 < person.Item1 && e.Item2 >= person.Item1),
(key, g) => new { Range = key, People = g.Select(k => k.Item2) })
输出
0-1500 汤姆
1501-2500 马克,保罗
2501-4000 安东尼,克里斯,罗恩