假设我有以下整数数组:
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作为第三组。
如果我指定要连续三个元素的第二组,查询应该返回给我第二组。
谢谢。
发布于 2011-08-18 18:48:06
更新:虽然技术上不是帕特里克在评论中指出的"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>();
}
}
}发布于 2011-08-18 18:56:10
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)发布于 2011-08-18 21:10:41
为什么不试试这个扩展方法呢?
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要求。
编辑:,或者这是较少的单词版本(它不使用计数方法):
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);
}https://stackoverflow.com/questions/7112435
复制相似问题