【问题标题】:How to check if all list items have the same value and return it, or return an “otherValue” if they don’t?如何检查所有列表项是否具有相同的值并返回它,或者如果没有则返回“otherValue”?
【发布时间】:2010-12-08 17:29:20
【问题描述】:

如果列表中的所有项目都具有相同的值,那么我需要使用该值,否则我需要使用“otherValue”。我想不出一个简单明了的方法来做到这一点。当列表为空时,它应该返回“其他”值。

另见Neat way to write loop that has special logic for the first item in a collection.

【问题讨论】:

标签: c# linq


【解决方案1】:
var val = yyy.First().Value;
return yyy.All(x=>x.Value == val) ? val : otherValue; 

我能想到的最干净的方式。您可以通过内联 val 使其成为单线,但 First() 将被评估 n 次,执行时间加倍。

要合并 cmets 中指定的“空集”行为,您只需在上述两行之前再添加一行:

if(yyy == null || !yyy.Any()) return otherValue;

【讨论】:

  • +1,如果有不同的值,使用.Any 是否允许枚举提前退出?
  • @adrift: All 将在遇到x.Value != val 的序列中的元素x 时立即终止。同样,Any(x => x.Value != val) 将在遇到x.Value != val 所在序列的元素x 时终止。也就是说,AllAny 都表现出类似于&&|| 的“短路”(这实际上是AllAny 的情况)。
  • @Jason:完全正确。 All(condition) 实际上是 !Any(!condition),一旦知道答案,对 any(!condition) 的评估就会终止。
  • 微优化:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
【解决方案2】:

人人平等的快速测试:

collection.Distinct().Count() == 1

【讨论】:

  • 这不适用于任何Class,尽管它应该适用于结构。不过,非常适合列出原语。
  • +1 比 KeithS 的解决方案 IMO 干净得多。如果你想允许空集合,你可能想使用collection.Distinct().Count() <= 1
  • 小心,.Distinct() 并不总是按预期工作 - 特别是当您使用对象时,请参阅this 问题。在这种情况下,您需要实现 IEquatable 接口。
  • 更简洁,是的,但在一般情况下性能较差; Distinct() 保证遍历集合中的每个元素一次,并且在每个元素不同的最坏情况下,Count() 将遍历完整列表两次。 Distinct() 还创建了一个 HashSet,因此它的行为可以是线性的,而不是 NlogN 或更糟,这会增加内存使用量。 All() 在所有元素相等的最坏情况下进行一次完整传递,并且不会创建任何新集合。
  • @KeithS 我希望你现在意识到,Distinct 根本不会遍历集合,Count 将通过Distinct 的迭代器进行一次遍历。
【解决方案3】:

虽然您当然可以使用现有的序列运算符构建这样的设备,但在这种情况下,我倾向于将其编写为自定义序列运算符。比如:

// Returns "other" if the list is empty.
// Returns "other" if the list is non-empty and there are two different elements.
// Returns the element of the list if it is non-empty and all elements are the same.
public static int Unanimous(this IEnumerable<int> sequence, int other)
{
    int? first = null;
    foreach(var item in sequence)
    {
        if (first == null)
            first = item;
        else if (first.Value != item)
            return other;
    }
    return first ?? other;
}

这很清楚,很简短,涵盖了所有情况,并且不会不必要地创建序列的额外迭代。

把它变成一个适用于IEnumerable&lt;T&gt; 的通用方法留作练习。 :-)

【讨论】:

  • 比如说,你有一个可以为空的序列,并且提取的值也是一个可以为空的。在这种情况下,序列可能是空的序列中的每个项目在提取的值中可能有一个空值。在这种情况下,合并将返回 other,而 null 实际上是(可能)正确的响应。假设函数是T Unanimous&lt;U, T&gt;(this IEnumerable&lt;U&gt; sequence, T other) 或一些这样的签名,这有点复杂。
  • @Anthony:确实,这里有很多复杂性,但它们很容易解决。为了方便起见,我使用了一个可为空的 int,这样我就不必声明“我已经看到第一项”标志了。您可以轻松地声明标志。此外,我使用“int”而不是 T,因为我知道您始终可以比较两个 int 是否相等,而两个 T 则不是这种情况。这更像是一个解决方案的草图,而不是一个功能齐全的通用解决方案。
【解决方案4】:
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

或者,如果您担心为每个元素执行 First()(这可能是一个有效的性能问题):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;

【讨论】:

  • @KeithS - 这就是我添加答案的第二部分的原因。在小型集合上,调用 First() 是微不足道的。在大型收藏中,这可能会成为一个问题。
  • “在小型集合中,调用 First() 是微不足道的。” - 这取决于集合的来源。对于简单对象的列表或数组,您是绝对正确的。但是,一些可枚举不是有限的内存缓存原语集。代表的集合或通过算法级数计算(例如斐波那契)产生的枚举器每次评估 First() 都会变得非常昂贵。
  • 或者更糟的是,如果查询是数据库查询并且每次调用“First”都会再次访问数据库。
  • 当你有像从文件中读取这样的一次性迭代时,情况会变得更糟......所以 Ani 来自其他线程的答案看起来最好。
  • @Eric - 来吧。每个元素打三遍数据库没什么问题...:-P
【解决方案5】:

这可能晚了,但根据 Eric 的回答,一个适用于值和引用类型的扩展:

public static partial class Extensions
{
    public static Nullable<T> Unanimous<T>(this IEnumerable<Nullable<T>> sequence, Nullable<T> other, IEqualityComparer comparer = null)  where T : struct, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (Nullable<T>)first ?? other;
    }

    public static T Unanimous<T>(this IEnumerable<T> sequence, T other, IEqualityComparer comparer = null)  where T : class, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (T)first ?? other;
    }
}

【讨论】:

    【解决方案6】:
    public int GetResult(List<int> list){
    int first = list.First();
    return list.All(x => x == first) ? first : SOME_OTHER_VALUE;
    }
    

    【讨论】:

      【解决方案7】:

      使用 LINQ 的替代方法:

      var set = new HashSet<int>(values);
      return (1 == set.Count) ? values.First() : otherValue;
      

      我发现使用 HashSet&lt;T&gt; 对于最多 6,000 个整数的列表相比更快:

      var value1 = items.First();
      return values.All(v => v == value1) ? value1: otherValue;
      

      【讨论】:

      • 首先这可能会产生大量垃圾。此外,它比其他 LINQ 答案不太清楚,但比扩展方法的答案慢。
      • 是的。但是,如果我们谈论确定一小组值是否都相同,那将不会是太多垃圾。当我在 LINQPad 中针对一小组值运行此语句和 LINQ 语句时,HashSet 更快(使用 Stopwatch 类计时)。
      • 如果你从命令行在发布版本中运行它,你可能会得到不同的结果。
      • 创建了一个控制台应用程序,发现 HashSet&lt;T&gt; 最初比在我的答案中使用 LINQ 语句更快。但是,如果我在循环中执行此操作,那么 LINQ 会更快。
      • 此解决方案的 问题是,如果您使用自定义类,则必须实现自己的GetHashCode(),这很难正确执行请参阅: stackoverflow.com/a/371348/2607840 了解更多详情。
      【解决方案8】:

      与上述简化方法略有不同。

      var result = yyy.Distinct().Count() == yyy.Count();

      【讨论】:

      • 这恰恰相反。这将检查列表中的每个元素是否都是唯一的。
      【解决方案9】:

      如果一个数组是像下面这样的多维类型,那么我们必须在 linq 下面写来检查数据。

      示例:这里的元素为 0,我正在检查所有值是否为 0。
      ip1=
      0 0 0 0
      0 0 0 0
      0 0 0 0
      0 0 0 0

          var value=ip1[0][0];  //got the first index value
          var equalValue = ip1.Any(x=>x.Any(xy=>xy.Equals()));  //check with all elements value 
          if(equalValue)//returns true or false  
          {  
          return "Same Numbers";  
          }else{  
          return "Different Numbers";   
          }
      

      【讨论】:

        猜你喜欢
        • 2022-11-27
        • 2021-09-10
        • 2019-12-23
        • 2013-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-03
        • 1970-01-01
        相关资源
        最近更新 更多