假设我有一个包含Value
类型对象的列表。 Value
有一个Name
属性:
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" },
};
现在我想获得所有“重复”值的列表(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;
}
但是因为我想在更复杂的环境中使用这个查询,所以可能有一个“linqish”解决方案。
答案 0 :(得分:7)
沿着这些方向不会有任何内置的东西,但是如果你经常需要它,你可以推出一些定制但相当通用的东西:
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;
}
}
}
}
用法:
foreach (Value value in values.WhereRepeated(x => x.Name))
{
Console.WriteLine(value.Name);
}
您可能想要考虑如何处理三胞胎等等 - 目前除了第一个之外的所有内容都会产生(与您的描述相符),但这可能不太正确。
答案 1 :(得分:4)
您可以实现Zip extension,然后使用.Skip(1)压缩列表,然后选择匹配的行。
这应该有效并且相当容易维护:
values
.Skip(1)
.Zip(items, (first,second) => first.Name==second.Name?first:null)
.Where(i => i != null);
此方法的一个小缺点是您遍历列表两次。
答案 2 :(得分:1)
我认为这可行(未经测试) - 这将为您提供重复的单词和它的索引。对于多次重复,您可以遍历此列表并检查连续索引。
var query = values.Where( (v,i) => values.Count > i+1 && v == values[i+1] )
.Select( (v,i) => new { Value = v, Index = i } );
答案 3 :(得分:1)
如果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;
答案 4 :(得分:1)
我知道这个问题很古老,但我只是在做同样的事情......
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);
}
}
应该适用于任何事情 - 只需提供一个比较元素的功能
答案 5 :(得分:-1)
您可以使用GroupBy扩展程序执行此操作。
答案 6 :(得分:-1)
像这样的东西
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,values = {elements with name}
免责声明:我没有对上述情况进行测试,所以我可能会离开。