如何对比IEnumerable<T>中元素与相邻下一个元素实现连续去重?
该需求完全可以通过LINQ实现,你之前写法的问题是LINQ默认遍历不会暴露元素的相邻节点属性,我们可以通过自带的LINQ方法构造相邻元素的对比关系,实现连续去重逻辑。
方案1:Zip方法实现(完全匹配你描述的实现逻辑)
核心逻辑为将原序列与跳过第一个元素的序列配对,逐对对比相邻元素,当前元素与下一个元素不同时保留当前元素,最后追加序列的最后一个元素即可:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) { // 空序列直接返回空 if (iterable == null || !iterable.Any()) return Enumerable.Empty<T>(); return iterable.Zip(iterable.Skip(1), (current, next) => new { current, next }) .Where(pair => !EqualityComparer<T>.Default.Equals(pair.current, pair.next)) .Select(pair => pair.current) .Concat(new[] { iterable.Last() }); }
方案2:带索引的Where重载(写法更简洁)
通过对比当前元素与前一个位置的元素是否相等,决定是否保留当前元素,第一个元素默认保留:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) { if (iterable == null || !iterable.Any()) return Enumerable.Empty<T>(); return iterable.Where((item, index) => index == 0 || !EqualityComparer<T>.Default.Equals(item, iterable.ElementAt(index - 1)) ); }
单次遍历最优实现(适配仅支持一次遍历的序列)
如果你的输入序列是仅支持单次遍历的特殊类型(如数据流包装的序列),上述LINQ方案会触发多次遍历,这种场景下可以用自定义迭代器实现,性能最优:
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) { if (iterable == null) yield break; using var enumerator = iterable.GetEnumerator(); if (!enumerator.MoveNext()) yield break; T lastValue = enumerator.Current; yield return lastValue; while (enumerator.MoveNext()) { T current = enumerator.Current; if (!EqualityComparer<T>.Default.Equals(current, lastValue)) { yield return current; lastValue = current; } } }
验证效果
输入AAABBAACCC序列调用方法:
var result = new string(UniqueInOrder("AAABBAACCC").ToArray()); // 输出结果为 ABAC,完全符合预期
内容的提问来源于stack exchange,提问作者Dorin
相关产品推荐
相关产品推荐

