我有一个C#中的有序数字列表,我想用LINQ计算可以根据其安全值获取的最小和最大值
列表始终是有序的,永远不会为空。
例如:
我的列表对象:
1060
1061
....
1089
1090
6368
6369
....
6383
6384
30165
30166
....
30214
30215
我的预期结果:
1060-1090
6368-6384
30165-30215
谢谢。
答案 0 :(得分:1)
对于此类问题,Zip
方法很方便。这就是它的作用:
将指定的函数应用于两个序列的相应元素,产生结果序列。
通过将序列与自身压缩在一起,可用于将序列的连续元素配对。
fn counting_function() -> u32 {
static mut counter: u32 = 0;
let retval = unsafe { counter };
unsafe {
counter += 1;
}
retval
}
输出:
结果:1-5、11-13、21-22
答案 1 :(得分:1)
请考虑为IEnumerable<TSource>
创建扩展方法,因此可以像使用LINQ函数一样使用它。参见Extension Methods Demystified
您的示例没有处理几个问题:
因此,我们给出一个适当的要求:
给出一个整数输入序列,创建一个整数对输出序列,其中值是输入序列中连续数字序列的第一个和最后一个数字。
示例:
我将成对的序列作为Tuple<int, int>
的序列返回。如果需要,您可以为此创建一个专用的类。
static IEnumerable<Tuple<int, int>> ToMinMaxTuples(this IEnumerable<int> source)
{
// TODO: source == null
var enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
// there is at least one item in source
int min = enumerator.Current;
int max = min;
while (enumerator.MoveNext())
{
// there is another item in the sequence
if (enumerator.Current == max + 1)
{
// current is part of the current sequence, continue with next number
max = enumerator.Current;
}
else
{
// current is not part of the current sequence,
// it is the start of the next one
// yield return [min, max] as a Tuple:
yield return new Tuple<int, int>(min, max);
// start the next sequence:
min = enumerator.Current;
max = min;
}
}
}
}
用法:
IEnumerable<Tuple<int, int>> result = myInputList.ToMinMaxTuples();
或者在一些大型LINQ语句中间:
var result = Students
.Where(student => student.Country == "Republique Française")
.Select(student => student.Grade)
.ToMinMaxTuples()
.OrderBy(tuple => tuple.Item1)
.ThenBy(tuple => tuple.Item2);
答案 2 :(得分:1)
//Sample list of ordered integers
List<int> lst = new List<int>{101,102,103,104,106,107,108,111,112,114,120,121};
// find minimum element of each sub-sequence within the above list
var minBoundaries = lst.Where(i => !lst.Contains(i-1)).ToList();
// find maximum element of each sub-sequence within the above list
var maxBoundaries = lst.Where(i => !lst.Contains(i+1)).ToList();
//format minimum and maximum elements of each sub-sequence as per the sample output in the question
var result = new List<string>();
for(int i = 0; i < maxBoundaries.Count; i++)
result.Add(minBoundaries[i]+"-"+maxBoundaries[i]);
答案 3 :(得分:0)
如果实现简单的配对类,则可以使用.Aggregate()
LINQ方法。
由于Tuple是不可变的,因此有必要使用对类,但是可以很容易地构造成这样。
public class MinMaxPair<T>
{
public MinMaxPair(T min, T max)
{
Min = min;
Max = max;
}
public T Min;
public T Max;
}
然后在适当的位置进行.Aggregate()
调用
nums.Aggregate(
new List<MinMaxPair<int>>(),
(sets, next) =>
{
if (!sets.Any() || next - sets.Last().Max > 1)
{
sets.Add(new MinMaxPair<int>(next, next));
}
else
{
var minMax = sets.Last();
if (next < minMax.Min)
minMax.Min = next;
else
minMax.Max = next;
}
return sets;
});
答案 4 :(得分:0)
使用Scan
扩展方法的成对增强版本,该方法基于类似于APL扫描运算符的聚合方法,但返回中间结果,因此创建了变量广义分组方法。我使用GroupByPairsWhile
(先前已)为此类问题创建了GroupBySequential
方法。
public static class IEnumerableExt {
// TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
// PrevKeyItem.Key = Previous Key
// PrevKeyItem.Value = Previous Item
// curItem = Current Item
// returns new Key
public static IEnumerable<(TKey Key, T Value)> ScanToPairs<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator())
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
// bool testFn(T prevItem, T curItem)
// returns groups by runs of matching bool
public static IEnumerable<IGrouping<int, T>> GroupByPairsWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanToPairs(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
public static IEnumerable<IGrouping<int, int>> GroupBySequential(this IEnumerable<int> src) => src.GroupByPairsWhile((prev, cur) => prev + 1 == cur);
}
使用扩展方法,您的问题很简单:
var ans = src.GroupBySequential().Select(g => new { Min = g.Min(), Max = g.Max() });
这假定列表未排序。如果已知列表是有序的,则可以使用First()
和Last()
代替Min()
和Max()
。
注意:扩展方法可能看起来很复杂,但它们为多种不同类型的分组提供了基础,包括按相等项目的运行分组,按通用测试功能分组以及处理第一个分组的各种种子和结束策略。和成对工作时的最后一个元素。