2010-01-02 29 views
10

快速的问题,programmaticaly最好的方法是问“这个序列中是否只有一个元素满足X条件?”使用Linq?如何在LINQ中问“在LINQ中有没有满足条件的元素?

// Pretend that the .OneAndOnlyOne() method exists 
int[] sequence = new int[] { 1, 1, 2, 3, 5, 8 }; 
Assert.IsTrue(sequence.OneAndOnlyOne(x => x == 2); 
Assert.IsFalse(sequence.OneAndOnlyOne(x => x == 1); 

这样的事情可以做:

sequence.SingleOrDefault(x => x == 2) != null; 

但是这是一个有点麻烦。

我想我可以推出自己的扩展方法,但这似乎是我的代码中的一种常见模式,我想确保有一个很好的干净方法来做到这一点。有没有使用内置LINQ方法的方法?

+0

'SingleOrDefault'将引发InvalidOperationException :如果有多个比赛。 – SLaks 2010-01-02 23:32:56

+0

我推荐IsLone()作为扩展方法的可能替代名称。你也用同样的功能做两件事,过滤然后检测是否只有一个元素。我会这样做:sequence.Where(x => x == 2).IsLone() – ICR 2010-01-03 01:39:00

回答

29

让你可以做什么:

bool onlyOne = source.Where(/*condition*/).Take(2).Count() == 1 

,这将阻止在发生多次匹配时计算不必要的大量序列。

+0

+1,我希望我可以给你另外一个+1。'Take(2)'部分(这非常合理。) – 2010-01-03 00:11:22

+0

嘿,我有点像。我可能会在我自己的扩展方法后面加上别名。 – Mike 2010-01-03 00:21:14

+0

你为什么拿2个元素?这背后有什么意义? ;) – citronas 2010-01-03 00:30:40

2

最简单的方法就是使用Count。 Single不会为你工作,因为如果不只有那个单一元素,它会抛出一个异常。

LBushkin建议(在评论中)使用SequenceEqual来比较序列与另一个序列。你可以使用,通过跳过与跳过(1)第一个元素,并比较结果序列为空序列,例如你可以从Empty

+2

是的,但是如果我的.OneAndOnlyOne()方法在一个序列上运行,并击中满足谓词的第二个元素 - 它可以返回错误的权利远。 Count()将枚举整个序列 – Mike 2010-01-02 23:35:12

+0

这是事实,但是“超出”Count可能是过早优化的情况 - 当然,取决于您的需要。如果你想防止这种情况,你可以使用.Skip(1),看看你是否得到一个空序列。 – 2010-01-02 23:38:11

+0

@Michael:你不需要使用Count。你可以使用'SequenceEqual()'来处理空序列,而不会抛出异常。而且,您可以随时推出自己的扩展方法......并不是OP对于使用情况来说非常必要。 – LBushkin 2010-01-02 23:52:00

0

你可以使用这个扩展方法,我认为应该已经包含在标准的扩展方法LINQ:

public static int CountUpTo<T>(this IEnumerable<T> sequence, int maxCount) { 
    if (sequence == null) throw new ArgumentNullException("sequence"); 
    if (maxCount < 0) throw new ArgumentOutOfRangeException("maxCount"); 

    var count = 0; 
    var enumerator = sequence.GetEnumerator(); 
    while (count < maxCount && enumerator.MoveNext()) 
     count += 1; 
    return count; 
} 

使用像这样:

return sequence.CountUpTo(2) == 1; 
相关问题