假设我有以下整数数组:
int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67,33, 13, 8, 12, 41, 5 };
我怎样才能编写Linq查询,找到3个连续元素,例如大于10?此外,如果我可以指定我想要说出这些元素的第一,第二,第三等组,那将是很好的。
例如,Linq查询应该能够识别: 12,15,17为第一组连续元素 23,25,27为第二组 67,33,13为第三组
如果我指定我想要第二组3个连续元素,那么查询应该返回给我第二组。
感谢。
答案 0 :(得分:7)
更新:虽然帕特里克在评论中指出,技术上不是“linq查询”,但此解决方案可重用,灵活且通用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication32
{
class Program
{
static void Main(string[] args)
{
int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67,33, 13, 8, 12, 41, 5 };
var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3);
foreach (var group in consecutiveGroups)
{
Console.WriteLine(String.Join(",", group));
}
}
}
public static class Extensions
{
public static IEnumerable<IEnumerable<T>> FindConsecutiveGroups<T>(this IEnumerable<T> sequence, Predicate<T> predicate, int count)
{
IEnumerable<T> current = sequence;
while (current.Count() > count)
{
IEnumerable<T> window = current.Take(count);
if (window.Where(x => predicate(x)).Count() >= count)
yield return window;
current = current.Skip(1);
}
}
}
}
输出:
12,15,17
23,25,27
67,33,13
要获得第二组,请更改:
var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3);
要:
var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3).Skip(1).Take(1);
更新2 在我们的生产用途中对此进行调整后,随着数字数组中项目数量的增加,以下实现速度会快得多。
public static IEnumerable<IEnumerable<T>> FindConsecutiveGroups<T>(this IEnumerable<T> sequence, Predicate<T> predicate, int sequenceSize)
{
IEnumerable<T> window = Enumerable.Empty<T>();
int count = 0;
foreach (var item in sequence)
{
if (predicate(item))
{
window = window.Concat(Enumerable.Repeat(item, 1));
count++;
if (count == sequenceSize)
{
yield return window;
window = window.Skip(1);
count--;
}
}
else
{
count = 0;
window = Enumerable.Empty<T>();
}
}
}
答案 1 :(得分:3)
int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67, 33, 13, 8, 12, 41, 5 };
var numbersQuery = numbers.Select((x, index) => new { Index = index, Value = x});
var query = from n in numbersQuery
from n2 in numbersQuery.Where(x => n.Index == x.Index - 1).DefaultIfEmpty()
from n3 in numbersQuery.Where(x => n.Index == x.Index - 2).DefaultIfEmpty()
where n.Value > 10
where n2 != null && n2.Value > 10
where n3 != null && n3.Value > 10
select new
{
Value1 = n.Value,
Value2 = n2.Value,
Value3 = n3.Value
};
为了指定哪个组,您可以调用Skip
方法
query.Skip(1)
答案 2 :(得分:0)
为什么不尝试这种扩展方法?
public static IEnumerable<IEnumerable<T>> Consecutives<T>(this IEnumerable<T> numbers, int ranges, Func<T, bool> predicate)
{
IEnumerable<T> ordered = numbers.OrderBy(a => a).Where(predicate);
decimal n = Decimal.Divide(ordered.Count(), ranges);
decimal max = Math.Ceiling(n); // or Math.Floor(n) if you want
return from i in Enumerable.Range(0, (int)max)
select ordered.Skip(i * ranges).Take(ranges);
}
唯一要改进的可能是调用Count
方法,因为导致numbers
的枚举(因此查询失去了它的懒惰)。
无论如何,我确信这符合您的linqness
要求。
编辑:或者这是 less words 版本(它没有使用Count方法):
public static IEnumerable<IEnumerable<T>> Consecutives<T>(this IEnumerable<T> numbers, int ranges, Func<T, bool> predicate)
{
var ordered = numbers.OrderBy(a => a);
return ordered.Where(predicate)
.Select((element, i) => ordered.Skip(i * ranges).Take(ranges))
.TakeWhile(Enumerable.Any);
}
答案 3 :(得分:0)
我必须为双打列表执行此操作。有上限和下限。这也不是真正的Linq解决方案,它只是我用脚本语言编写的一种实用方法,它只实现了C#的一个子集。
var sequence =
[0.25,0.5,0.5,0.5,0.7,0.8,0.7,0.9,0.5,0.5,0.8,0.8,0.5,0.5,0.65,0.65,0.65,0.65,0.65,0.65,0.65];
double lowerLimit = 0.1;
double upperLimit = 0.6;
int minWindowLength = 3;
// return type is a list of lists
var windows = [[0.0]];
windows.Clear();
int consec = 0;
int index = 0;
while (index < sequence.Count){
// store segments here
var window = new System.Collections.Generic.List<double>();
while ((index < sequence.Count) && (sequence[index] > upperLimit || sequence[index] < lowerLimit)) {
window.Add(sequence[index]);
consec = consec + 1;
index = index +1;
}
if (consec > minWindowLength) {
windows.Add(window);
}
window = new System.Collections.Generic.List<double>();
consec = 0;
index = index+1;
}
return windows;