使用linq从List 中删除连续重复项

我正在寻找一种方法来防止重复列表中的项目但仍保留订单。 例如

1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 

应该成为

 1, 2, 3, 4, 1, 2, 3, 4 

我使用for循环非常优雅地完成它,检查下一项如下

  public static List RemoveSequencialRepeats(List input) { var result = new List(); for (int index = 0; index < input.Count; index++) { if (index == input.Count - 1) { result.Add(input[index]); } else if (!input[index].Equals(input[index + 1])) { result.Add(input[index]); } } return result; } 

是否有更优雅的方式来做到这一点,最好是LINQ?

您可以创建扩展方法:

 public static IEnumerable RemoveSequentialRepeats( this IEnumerable source) { using (var iterator = source.GetEnumerator()) { var comparer = EqualityComparer.Default; if (!iterator.MoveNext()) yield break; var current = iterator.Current; yield return current; while (iterator.MoveNext()) { if (comparer.Equals(iterator.Current, current)) continue; current = iterator.Current; yield return current; } } } 

用法:

 var result = items.RemoveSequentialRepeats().ToList(); 

你可以编写简单的LINQ:

 var l = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 }; var k = new Nullable(); var nl = l.Where(x => { var res = x != k; k = x; return res; }).ToArray(); int[8] { 1, 2, 3, 4, 1, 2, 3, 4 } 

或pythonic(嗯,我最好的尝试)方式:

 l.Zip(l.Skip(1), (x, y) => new[] { x, y }) .Where(z => z[0] != z[1]).Select(a => a[0]) .Concat(new[] { l[l.Length - 1] }).ToArray() int[8] { 1, 2, 3, 4, 1, 2, 3, 4 } 

最简单的一个( 编辑:还没有看到King King已经建议)

 l.Where((x, i) => i == l.Length - 1 || x != l[i + 1]).ToArray() int[8] { 1, 2, 3, 4, 1, 2, 3, 4 } 

如果你想要LINQ语句不依赖于调用中的结果的捕获值,你需要一些带有聚合的构造,因为它是唯一携带值和操作的方法。 即基于Zaheer Ahmed的代码:

 array.Aggregate(new List(), (items, element) => { if (items.Count == 0 || items.Last() != element) { items.Add(element); } return items; }); 

或者您甚至可以尝试构建列表, if

  array.Aggregate(Enumerable.Empty(), (items, element) => items.Concat( Enumerable.Repeat(element, items.Count() == 0 || items.Last() != element ? 1:0 )) ); 

注意为了使用Aggregate获得上述样本的合理性能,你还需要携带最后一个值( Last必须在每一步上迭代整个序列),但是在Tuple中携带3个值{IsEmpty, LastValue, Sequence}代码非常奇怪的看。 这些样品仅用于娱乐目的。

另一个选择是Zip数组本身移1并返回不相等的元素…

更实用的选择是构建过滤值的迭代器:

 IEnumerable NonRepeated(IEnumerable values) { string last = null; bool lastSet = false; foreach(var element in values) { if (!lastSet || last != element) { yield return element; } last = element; lastSet = true; } } 

如果你真的很讨厌这个世界,纯粹的LINQ:

 var nmbs = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4, 5 }; var res = nmbs .Take(1) .Concat( nmbs.Skip(1) .Zip(nmbs, (p, q) => new { prev = q, curr = p }) .Where(p => p.prev != p.curr) .Select(p => p.curr)); 

但请注意,您需要枚举(至少部分)可枚举的3次( ZipZip的“左”部分, Zip的第一个参数)。 此方法比构建yield方法或直接执行方法慢

说明:

  • 你取第一个数字( .Take(1)
  • 你从第二个中获取所有数字( .Skip(1) )并将它与所有数字( .Zip(nmbs )) .Zip(nmbs 。我们将把第一个“集合”中的数字称为curr ,并将第二个“集合中的数字称为” “( (p, q) => new { prev = q, curr = p }) )。 然后,您只获取与前一个数字不同的数字( .Where(p => p.prev != p.curr) ),然后从这些数字中取出curr值并丢弃prev值( .Select(p => p.curr)
  • 你连接这两个集合( .Concat(

您也可以使用纯LINQ

 List list = new List{1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4}; var result = list.Where((x, i) => i == 0 || x != list[i - 1]); 

检查新列表和当前项的最后一个是否相同然后添加到新列表:

 List results = new List(); results.Add(array.First()); foreach (var element in array) { if(results[results.Length - 1] != element) results.Add(element); } 

或使用LINQ:

 List arr=new List(){1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 }; List result = new List() { arr.First() }; arr.Select(x => { if (result[result.Length - 1] != x) result.Add(x); return x; }).ToList(); 

对null对象进行适当的validation。

试试这个:

 class Program { static void Main(string[] args) { var input = "1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 "; var list = input.Split(',').Select(i => i.Trim()); var result = list .Select((s, i) => (s != list.Skip(i + 1).FirstOrDefault()) ? s : null) .Where(s => s != null) .ToList(); } } 

这里是您需要的代码:

 public static List RemoveSequencialRepeats(List input) { var result = new List(); result.Add(input.First()); result.AddRange(input.Where(p_element => result.Last() != p_element); return result; } 

LINQ魔术是:

  result.Add(input.First()); result.AddRange(input.Where(p_element => result.Last() != p_element); 

或者你可以像这样创建扩展方法:

 public static class Program { static void Main(string[] args) { List numList=new List(){1,2,2,2,4,5,3,2}; numList = numList.RemoveSequentialRepeats(); } public static List RemoveSequentialRepeats(this List p_input) { var result = new List { p_input.First() }; result.AddRange(p_input.Where(p_element => !result.Last().Equals(p_element))); return result; } } 

如果您想引用F#项目,可以编写

 let rec dedupe = function | x::y::rest when x = y -> x::dedupe rest | x::rest -> x::dedupe rest | _ -> []