有效地将IEnumerable值折叠在一起
本文关键字:折叠 在一起 IEnumerable 有效地 | 更新日期: 2023-09-27 18:21:09
我想将某些IEnumerable
中的值"折叠"在一起,以便将相同的相邻元素折叠为单个元素。
我想不出比举个例子更好的方法来描述这个问题了:
数组[0,0,2,0,1,1,2,2,1,0,2,1,0,0,1,0,1,1,1,1]应变为[0,2,0,1,2,1,0,2,1,0,1]
在我的用例中,这需要在关键循环中发生,因此必须尽可能快。我可以循环遍历数组,并对照前一个元素检查每个元素,如果它是重复的,则删除它,但我希望有一种更快的方法。
我的使用将仅用于相对较短的阵列(<100个元素),并且仅使用int
,然而,将赞赏通用解决方案。
编辑:正如下面所指出的,问题从根本上来说是O(n)复杂性,但我希望linqy可以击败我(可能很笨拙)的实现。
如果您想要一个通用的解决方案,请编写一个扩展方法:
这应该做得很好:
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
循环来完成它。
我可以循环遍历数组,并对照前一个元素检查每个元素,如果它是重复的,则删除它,但我希望有一种更快的方法。
从根本上讲,永远不会有一种方法在算法上更快。使用相同算法的实现可能会有细微的差异,但这是最好的。没有办法避免检查每个项目,所以无论你做什么,操作都将是O(n)。
您可以使用MSDN上提供的ChunkBy
扩展。然后很容易:
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());