【问题标题】:Check that all items of IEnumerable<T?> has the same value using LINQ使用 LINQ 检查 IEnumerable<T?> 的所有项是否具有相同的值
【发布时间】:2010-12-04 17:48:47
【问题描述】:

我有一个可以为空的类型,例如SomeEnum? 和一组值,例如IEnumerable&lt;SomeEnum?&gt;.

如何使用 LINQ 检查所有项目是否具有相同的值(并获取该值)?

【问题讨论】:

    标签: c# linq .net-3.5 ienumerable


    【解决方案1】:
    data.Distinct().Count() == 1;
    

    效果也很好。

    【讨论】:

    • 好主意。并且可以包裹在和李的方式一样的扩展方法中
    【解决方案2】:
    public static bool AllEqual<T>(this IEnumerable<T?> values) where T : struct
    {
            if (!values.Any()) return true;
            T? first = values.First();
            return values.Skip(1).All(v => first.Equals(v));
    }
    

    编辑:要获取值,您可以返回一个元组(成功,值),如下所示:

    public Tuple<bool, T?> AllEqual<T>(IEnumerable<T?> values) where T : struct
    {
        if(! values.Any()) return Tuple.Create(true, (T?)null);
        T? first = values.First();
        bool equal = values.Skip(1).All(v => v.Equals(first));
        return Tuple.Create(equal, equal ? first : (T?)null);
    }
    

    或者您可以使用 out 参数:

    public static bool AllEqual<T>(this IEnumerable<T?> values, out T? value) where T : struct
    {
        value = null;
        if (!values.Any()) return true;
    
        T? first = values.First();
        value = first;
    
        return values.Skip(1).All(v => first.Equals(v));
    }
    

    【讨论】:

    • 这对于 LINQ to Objects 来说是更可取的,因为它会在发现一个不相等的值时立即退出。不过,在 LINQ to Entities 等中,它会导致 3 次往返。
    • @stripling - 这个问题的所有答案都短路了。无论如何,EF 会将其视为 L2O,因为它不使用 Queryable,除非接口是 IQueryable
    • @Marc Gravell:说得好。我不知道 Distinct 是否肯定是惰性评估的。应该假设它是。
    • @stripling - 需要注意的是,由于并非所有序列都是可重复的,即使在 L2O 中,您也应该避免多次读取
    • @abatishchev - 假设您的意思是检查空序列的!values.Any() - 此实现假定空序列中的所有项目都是空的并且“值”为空。您可能想要抛出异常。 values.All(...) 返回序列中的每个项目是否满足某些条件 - 在这种情况下,每个其他项目是否等于第一个(因此都相等)。
    【解决方案3】:

    懒惰并为空序列返回 true:

    public static bool AllEqual<T>(this IEnumerable<T> source, out T value)
    {
        using (var enumerator = source.GetEnumerator())
        {
            if (!enumerator.MoveNext())
            {
                value = default(T);
                return true;
            }
    
            value = enumerator.Current;
            var comparer = EqualityComparer<T>.Default;
    
            while (enumerator.MoveNext())
            {
                if (!comparer.Equals(value, enumerator.Current))
                {
                    return false;
                }
            }
    
            return true;
        }
    }
    

    【讨论】:

    • +1 用于避免像 Distinct 这样的中间列表和像第一个答案这样的多次迭代,一些序列不支持。绝对是这个列表中最好的实现。
    • 我同意迈克的观点——非常好的解决方案。但不使用 LINQ,正如问题作者所问的那样。
    【解决方案4】:
    var value = data.Distinct().Single();
    

    (如果不存在唯一的唯一值,则会抛出异常,否则将返回该值)

    如果你不想要异常:

    var few=data.Distinct().Take(2).ToList();
    if(few.Count==1) {
        var value = few[0];
        ...
    }
    

    【讨论】:

    • Take(2) 如果Distinct() 返回 1-length 集合会抛出异常,不是吗?
    • @abatishchev:不,不会。它在“尽力而为”的基础上运作。
    • @abatishchev Take(n) 最多,而不是完全
    • @abatishchev - 还要注意这些是序列,而不是集合(直到 ToList)。一个微妙的区别,也许 - 直到你得到大量......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2020-02-03
    • 1970-01-01
    相关资源
    最近更新 更多