C# 使用Linq查找连续重复的元素
假设我有一个列表,其中包含类型为C# 使用Linq查找连续重复的元素,c#,linq,C#,Linq,假设我有一个列表,其中包含类型为Value的对象值具有名称属性: private List<Value> values = new List<Value> { new Value { Id = 0, Name = "Hello" }, new Value { Id = 1, Name = "World" }, new Value { Id = 2, Name = "World" }, new Value { Id = 3, Name = "
Value
的对象<代码>值具有名称
属性:
private List<Value> values = new List<Value> {
new Value { Id = 0, Name = "Hello" },
new Value { Id = 1, Name = "World" },
new Value { Id = 2, Name = "World" },
new Value { Id = 3, Name = "Hello" },
new Value { Id = 4, Name = "a" },
new Value { Id = 5, Name = "a" },
};
私有列表值=新列表{
新值{Id=0,Name=“Hello”},
新值{Id=1,Name=“World”},
新值{Id=2,Name=“World”},
新值{Id=3,Name=“Hello”},
新值{Id=4,Name=“a”},
新值{Id=5,Name=“a”},
};
现在我想获得所有“重复”值的列表(name属性与上一个元素的name属性相同的元素)。在本例中,我希望返回一个包含两个元素“world”和“a”(id=2和5)的列表 linq是否有可能举办此活动? 我当然可以这么说。像这样:
List<Value> tempValues = new List<Value>();
String lastName = String.Empty();
foreach (var v in values)
{
if (v.Name == lastName) tempValues.Add(v);
lastName = v.Name;
}
List tempValues=new List();
String lastName=String.Empty();
foreach(值中的var v)
{
如果(v.Name==lastName)tempValues.Add(v);
lastName=v.Name;
}
但由于我想在更复杂的上下文中使用此查询,可能有一个“linqish”解决方案。您可以实现一个,然后使用.Skip(1)压缩列表,然后选择匹配的行
这应该是可行的,并且很容易维护:
values
.Skip(1)
.Zip(items, (first,second) => first.Name==second.Name?first:null)
.Where(i => i != null);
此方法的一个轻微缺点是,您需要在列表中迭代两次 您可以使用GroupBy扩展来实现这一点。这些方面不会内置任何东西,但是如果您经常需要它,您可以推出一些定制但相当通用的东西:
static IEnumerable<TSource> WhereRepeated<TSource>(
this IEnumerable<TSource> source)
{
return WhereRepeated<TSource,TSource>(source, x => x);
}
static IEnumerable<TSource> WhereRepeated<TSource, TValue>(
this IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
using (var iter = source.GetEnumerator())
{
if (iter.MoveNext())
{
var comparer = EqualityComparer<TValue>.Default;
TValue lastValue = selector(iter.Current);
while (iter.MoveNext())
{
TValue currentValue = selector(iter.Current);
if (comparer.Equals(lastValue, currentValue))
{
yield return iter.Current;
}
lastValue = currentValue;
}
}
}
}
您可能想考虑如何处理三胞胎等-目前除了第一个之外的所有东西都将被生成(与您的描述相匹配),但这可能不太正确。我认为这是可行的(未经测试)-这将为您提供重复的单词及其索引。对于多次重复,您可以遍历此列表并检查连续索引
var query = values.Where( (v,i) => values.Count > i+1 && v == values[i+1] )
.Select( (v,i) => new { Value = v, Index = i } );
像这样的
var dupsNames =
from v in values
group v by v.Name into g
where g.Count > 1 // If a group has only one element, just ignore it
select g.Key;
应该有用。然后,您可以在第二个查询中使用结果:
dupsNames.Select( d => values.Where( v => v.Name == d ) )
这将返回一个key=name、value={elements with name}的分组
免责声明:我没有对上述内容进行测试,因此我可能有点不对劲。如果ID始终按照您的示例中的顺序排列,那么下面是另一种简单的方法:
var data = from v2 in values
join v1 in values on v2.Id equals v1.Id + 1
where v1.Name == v2.Name
select v2;
我知道这个问题很古老,但我只是在做同样的事情,所以
static class utils
{
public static IEnumerable<T> FindConsecutive<T>(this IEnumerable<T> data, Func<T,T,bool> comparison)
{
return Enumerable.Range(0, data.Count() - 1)
.Select( i => new { a=data.ElementAt(i), b=data.ElementAt(i+1)})
.Where(n => comparison(n.a, n.b)).Select(n => n.a);
}
}
静态类utils
{
公共静态IEnumerable FindConceutive(此IEnumerable数据,函数比较)
{
返回可枚举的.Range(0,data.Count()-1)
.Select(i=>new{a=data.ElementAt(i),b=data.ElementAt(i+1)})
。其中(n=>比较(n.a,n.b))。选择(n=>n.a);
}
}
应该适用于任何情况-只需提供一个函数来比较元素这将提取具有重复项的任何内容,而不仅仅是连续重复项。这比Zip方法更有效。但是我发现Zip方法读起来更好一些(它所做的事情更清楚),这对我来说并不重要。。。而且对一个将军也不起作用……很好——我喜欢:)@Sam:你说这不是LINQy是什么意思?这对我来说是非常危险的:)(或者如果你真的想得到techincal,Lambday..它可以在一瞬间变得危险):@Pure,如果值纯粹是一个IEnumerable(而不是IList),那么这是不起作用的,所以它是一个真正的特定解决方案,只适用于IList。但是,它确实符合规范并完成了任务。您可以使用ElementAt()扩展来处理通用枚举。不过,当我有其他案例时,我会担心以后的优化。这也是一个很好的解决方案。在我的例子中,性能不是问题(只有几百个元素)。我猜他的意思是
values.GroupBy(x=>x.Name)。其中(x=>x.Count()>0)。选择(x=>x.First())代码>
static class utils
{
public static IEnumerable<T> FindConsecutive<T>(this IEnumerable<T> data, Func<T,T,bool> comparison)
{
return Enumerable.Range(0, data.Count() - 1)
.Select( i => new { a=data.ElementAt(i), b=data.ElementAt(i+1)})
.Where(n => comparison(n.a, n.b)).Select(n => n.a);
}
}