有效地将IE可数值合并在一起

时间:2015-11-08 17:36:41

标签: c# arrays

我想将某些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,但是我们会理解通用解决方案。

编辑:正如下面所指出的,问题基本上是O(n)的复杂性,但我希望linqy可能会击败我的(可能是笨拙的)实现。

3 个答案:

答案 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());