我想将某些IEnumerable
中的值“折叠”在一起,使相同的相邻元素折叠为单个元素。
除了一个例子,我想不出更好的方式来描述问题:
数组[0,0,2,0,1,1,2,2,2,1,0,0,2,1,1,0,1,1,1] 应该变成[0,2,0,1,2,1,0,2,1,0,1]
在我的用例中,这需要在关键循环中发生,因此必须尽可能快。我可以循环遍历数组并检查每个元素与前一个元素,删除它是否重复,但我希望有更快的方法。
我的用途仅适用于相对较短的数组(< 100个元素),仅使用int
,但是我们会理解通用解决方案。
答案 0 :(得分:4)
我可以循环遍历数组并检查每个元素与前一个元素,如果它是重复的则删除,但我希望有更快的方法。
从根本上说,从来没有一种算法更快的方式。使用相同算法的实现可能存在细微差别,但这是您可以获得的最佳结果。没有办法避免检查每个项目,所以无论你做什么,操作都是O(n)。
答案 1 :(得分:4)
如果您想要通用解决方案,请编写扩展方法:
这应该做得很好:
public static IEnumerable<T> DistinctConsecutive<T>(this IEnumerable<T> sequence)
=> sequence.DistinctConsecutive(EqualityComparer<T>.Default);
public static IEnumerable<T> DistinctConsecutive<T>(this IEnumerable<T> sequence, IEqualityComparer<T> comparer)
{
if (sequence == null)
throw new ArgumentNullException(nameof(sequence));
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
return DistinctConsecutiveImpl(sequence, comparer);
}
private static IEnumerable<T> DistinctConsecutiveImpl<T>(IEnumerable<T> sequence, IEqualityComparer<T> comparer)
{
using (var enumerator = sequence.GetEnumerator())
{
if (!enumerator.MoveNext())
yield break;
var lastValue = enumerator.Current;
yield return lastValue;
while (enumerator.MoveNext())
{
var value = enumerator.Current;
if (comparer.Equals(lastValue, value))
continue;
yield return value;
lastValue = value;
}
}
}
或者 lazier 方法:
public static IEnumerable<T> DistinctConsecutive<T>(this IEnumerable<T> sequence, IEqualityComparer<T> comparer = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
using (var enumerator = sequence.GetEnumerator())
{
if (!enumerator.MoveNext())
yield break;
var lastValue = enumerator.Current;
yield return lastValue;
while (enumerator.MoveNext())
{
var value = enumerator.Current;
if (comparer.Equals(lastValue, value))
continue;
yield return value;
lastValue = value;
}
}
}
如果您需要优化的解决方案,请抛弃泛型并使用==
而不是IEqualityComparer<T>
。如果这仍然是瓶颈,请使用普通的旧for
循环。
答案 2 :(得分:0)
您可以使用ChunkBy
extension provided on MSDN。然后很容易:
var src = new[]{0, 0, 2, 0, 1, 1, 2, 2, 2, 1, 0, 0, 2, 1, 1, 0, 1, 1, 1};
var pruned = src.ChunkBy(x => x).Select(c => c.First());