我有一个号码清单,例如21,4,7,9,12,22,17,8,2,20,23
我希望能够选择序列序列(至少3项长度),所以从上面的例子中,它将是7,8,9和20,21,22,23。
我曾经玩过一些丑陋的、杂乱无章的功能,但我想知道是否有一种简洁的LINQ方法来实现它。
有什么建议吗?
更新:
非常感谢你的回复,非常感谢。我目前正在与他们所有的发挥,看看哪一个将最好地集成到我们的项目。
发布于 2010-10-02 06:20:26
我突然想到,你应该做的第一件事就是订购这份清单。然后,它只是一个问题,通过它,记住你的当前序列的长度,并检测它何时结束。老实说,我怀疑一个简单的foreach循环将是最简单的方法--我不能马上想到任何类似LINQ的奇妙的方法。如果你真的想要的话,你当然可以在迭代器块中这样做,但是要记住,命令列表从一开始就意味着你已经有了合理的“预先”成本。所以我的解决方案应该是这样的:
var ordered = list.OrderBy(x => x);
int count = 0;
int firstItem = 0; // Irrelevant to start with
foreach (int x in ordered)
{
// First value in the ordered list: start of a sequence
if (count == 0)
{
firstItem = x;
count = 1;
}
// Skip duplicate values
else if (x == firstItem + count - 1)
{
// No need to do anything
}
// New value contributes to sequence
else if (x == firstItem + count)
{
count++;
}
// End of one sequence, start of another
else
{
if (count >= 3)
{
Console.WriteLine("Found sequence of length {0} starting at {1}",
count, firstItem);
}
count = 1;
firstItem = x;
}
}
if (count >= 3)
{
Console.WriteLine("Found sequence of length {0} starting at {1}",
count, firstItem);
}编辑:好吧,我只是想到了一种更多的LINQ风格的做事方式。我现在没有时间完全实现它,但是:
SelectWithPrevious (可能更好的名称为SelectConsecutive)来获取连续的元素对,SelectWithPrevious获取两个起始点之间的序列长度(从previous)的任何序列减去)。
我怀疑您需要在有序的序列上连接int.MinValue,以确保最终的项被正确地使用。
编辑:好的,我已经实现了这一点。是关于我能想到做这个的LINQiest方法.我使用null值作为"sentinel“值来强制开始和结束序列--有关更多细节,请参见注释。
总的来说,我不推荐这个解决方案。这是很难让你的头,虽然我很有信心它是正确的,它花了我一段时间来思考可能的一个错误,等等。这是一个有趣的航程,你可以用LINQ.还有你可能不该做的事。
哦,请注意,我已经把“最小长度3”部分推到了调用方--当您有这样的一个元组序列时,单独过滤它是比较干净的,IMO。
using System;
using System.Collections.Generic;
using System.Linq;
static class Extensions
{
public static IEnumerable<TResult> SelectConsecutive<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
using (IEnumerator<TSource> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
TSource prev = iterator.Current;
while (iterator.MoveNext())
{
TSource current = iterator.Current;
yield return selector(prev, current);
prev = current;
}
}
}
}
class Test
{
static void Main()
{
var list = new List<int> { 21,4,7,9,12,22,17,8,2,20,23 };
foreach (var sequence in FindSequences(list).Where(x => x.Item1 >= 3))
{
Console.WriteLine("Found sequence of length {0} starting at {1}",
sequence.Item1, sequence.Item2);
}
}
private static readonly int?[] End = { null };
// Each tuple in the returned sequence is (length, first element)
public static IEnumerable<Tuple<int, int>> FindSequences
(IEnumerable<int> input)
{
// Use null values at the start and end of the ordered sequence
// so that the first pair always starts a new sequence starting
// with the lowest actual element, and the final pair always
// starts a new one starting with null. That "sequence at the end"
// is used to compute the length of the *real* final element.
return End.Concat(input.OrderBy(x => x)
.Select(x => (int?) x))
.Concat(End)
// Work out consecutive pairs of items
.SelectConsecutive((x, y) => Tuple.Create(x, y))
// Remove duplicates
.Where(z => z.Item1 != z.Item2)
// Keep the index so we can tell sequence length
.Select((z, index) => new { z, index })
// Find sequence starting points
.Where(both => both.z.Item2 != both.z.Item1 + 1)
.SelectConsecutive((start1, start2) =>
Tuple.Create(start2.index - start1.index,
start1.z.Item2.Value));
}
}发布于 2010-10-02 09:49:44
我认为我的解决方案更优雅、更简单,因此更容易验证为正确:
/// <summary>Returns a collection containing all consecutive sequences of
/// integers in the input collection.</summary>
/// <param name="input">The collection of integers in which to find
/// consecutive sequences.</param>
/// <param name="minLength">Minimum length that a sequence should have
/// to be returned.</param>
static IEnumerable<IEnumerable<int>> ConsecutiveSequences(
IEnumerable<int> input, int minLength = 1)
{
var results = new List<List<int>>();
foreach (var i in input.OrderBy(x => x))
{
var existing = results.FirstOrDefault(lst => lst.Last() + 1 == i);
if (existing == null)
results.Add(new List<int> { i });
else
existing.Add(i);
}
return minLength <= 1 ? results :
results.Where(lst => lst.Count >= minLength);
}优于其他解决方案:
发布于 2010-10-02 07:18:02
下面是如何以"LINQish“的方式解决问题:
int[] arr = new int[]{ 21, 4, 7, 9, 12, 22, 17, 8, 2, 20, 23 };
IOrderedEnumerable<int> sorted = arr.OrderBy(x => x);
int cnt = sorted.Count();
int[] sortedArr = sorted.ToArray();
IEnumerable<int> selected = sortedArr.Where((x, idx) =>
idx <= cnt - 3 && sortedArr[idx + 1] == x + 1 && sortedArr[idx + 2] == x + 2);
IEnumerable<int> result = selected.SelectMany(x => new int[] { x, x + 1, x + 2 }).Distinct();
Console.WriteLine(string.Join(",", result.Select(x=>x.ToString()).ToArray()));由于数组的复制和重构,这种解决方案--当然--并不像传统的循环解决方案那样有效。
https://stackoverflow.com/questions/3844611
复制相似问题