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);
    }
}