【问题标题】:LINQ to count Continues repeated items(int) in an int Array?LINQ 计数在 int 数组中继续重复项(int)?
【发布时间】:2012-07-05 00:22:14
【问题描述】:

这是我的问题的一个场景:我有一个数组,比如说:

{ 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }

结果应该是这样的(数组元素 => 它的计数):

4 => 1
1 => 2
3 => 2
2 => 1
5 => 1
3 => 1
2 => 2

我知道这可以通过for loop来实现。

但是 google 做了很多努力以使用 LINQ 使用较少的代码行来实现这一点,但没有成功。

【问题讨论】:

  • 如果一个例子是 4, 1, 1, 3, 3, 2, 5, 3, 2, 2...,很难看出如何将数组长度固定为 6...
  • 现在请检查问题
  • 为什么输出中有重复的数字“2”和“3”键?您不应该期望输出为“4 => 1, 1 => 2, 3 => 3, 2 => 3, 5 =>1”吗?
  • 你真的在找run-length encoding吗?
  • @sandeep:这不是真的“当然”——你的问题不清楚,因为你从不使用“连续”这个词,这是这里的重要一点。假设您的意思只是计算元素的出现次数,三个人全部回答了这个问题,这一事实表明您应该进行编辑以使其更清晰......

标签: c# .net linq algorithm


【解决方案1】:
var array = new int[] {1,1,2,3,5,6,6 };
var arrayd = array.Distinct();
var arrayl= arrayd.Select(s => { return array.Where(s2 => s2 == s).Count(); }).ToArray();

输出

arrayl=[0]2 [1]1 [2]1 [3]1 [4]2

【讨论】:

  • 欢迎来到 Stack Overflow!花点时间阅读How to Answer - 这看起来很有帮助,但它会受益于对代码作用的一些解释,考虑edit-ing 吗?
【解决方案2】:
var array = new int[] {1,1,2,3,5,6,6 };
foreach (var g in array.GroupBy(i => i))
{
    Console.WriteLine("{0} => {1}", g.Key, g.Count());
}

【讨论】:

  • 最好在代码中包含一些上下文/解释,因为这会使答案对 OP 和未来的读者更有用。
【解决方案3】:

看哪(你可以直接在 LINQPad 中运行它——rle 是魔法发生的地方):

var xs = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };

var rle = Enumerable.Range(0, xs.Length)
                    .Where(i => i == 0 || xs[i - 1] != xs[i])
                    .Select(i => new { Key = xs[i], Count = xs.Skip(i).TakeWhile(x => x == xs[i]).Count() });

Console.WriteLine(rle);

当然,这是 O(n^2),但你没有在规范中要求线性效率。

【讨论】:

    【解决方案4】:

    我已经在there 上写了你需要的方法。这是如何调用它。

    foreach(var g in numbers.GroupContiguous(i => i))
    {
      Console.WriteLine("{0} => {1}", g.Key, g.Count);
    }
    

    【讨论】:

      【解决方案5】:

      这是一个有效的 LINQ 表达式(编辑:稍微收紧代码):

      var data = new int[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
      var result = data.Select ((item, index) =>
                              new
                              {
                                  Key = item,
                                  Count = (index == 0 || data.ElementAt(index - 1) != item) 
                                      ? data.Skip(index).TakeWhile (d => d == item).Count ()
                                      : -1
                              }
                                )
                        .Where (d => d.Count != -1);
      

      here's a proof 表明它工作正常。

      【讨论】:

      • 在某种程度上这是正确的答案,因为它使用问题中所述的 LINQ。对这种方法的效率感到好奇,我计算了在给定 10 个元素的输入数组的情况下创建的枚举数和访问的项目数。与创建 1 个枚举器并访问 10 个元素的最佳方法相比,创建了 17 个枚举器并访问了 101 个元素。
      【解决方案6】:

      我相信最优化的方法是使用迭代器块创建“类似 LINQ”的扩展方法。这允许您执行计算,对数据进行单次传递。请注意,如果您只想对一小部分数字执行计算,那么性能根本不重要。当然这真的是你变相的 for 循环。

      static class Extensions {
      
        public static IEnumerable<Tuple<T, Int32>> ToRunLengths<T>(this IEnumerable<T> source) {
          using (var enumerator = source.GetEnumerator()) {
            // Empty input leads to empty output.
            if (!enumerator.MoveNext())
              yield break;
      
            // Retrieve first item of the sequence.
            var currentValue = enumerator.Current;
            var runLength = 1;
      
            // Iterate the remaining items in the sequence.
            while (enumerator.MoveNext()) {
              var value = enumerator.Current;
              if (!Equals(value, currentValue)) {
                // A new run is starting. Return the previous run.
                yield return Tuple.Create(currentValue, runLength);
                currentValue = value;
                runLength = 0;
              }
              runLength += 1;
            }
      
            // Return the last run.
            yield return Tuple.Create(currentValue, runLength);
          }
        }
      
      }
      

      请注意,扩展方法是通用的,您可以在任何类型上使用它。使用Object.Equals 比较值是否相等。但是,如果您愿意,可以传递 IEqualityComparer&lt;T&gt; 以允许自定义值的比较方式。

      你可以使用这样的方法:

      var numbers = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
      var runLengths = numbers.ToRunLengths();
      

      对于您输入的数据,结果将是这些元组:

      4 1 1 2 3 2 2 1 5 1 3 1 2 2

      【讨论】:

      • 使用GetEnumerator 是否有优势,或者只是按照要求避免使用foreach
      • @Jodrell:因为我必须对第一个元素进行特殊处理,所以它必须超出主循环。使用枚举器允许我这样做,并且仍然只检索和检查每个元素一次。
      【解决方案7】:

      这还不够短?

      public static IEnumerable<KeyValuePair<T, int>> Repeats<T>(
              this IEnumerable<T> source)
      {
          int count = 0;
          T lastItem = source.First();
      
          foreach (var item in source)
          {
              if (Equals(item, lastItem))
              {
                  count++;
              }
              else
              {
                 yield return new KeyValuePair<T, int>(lastItem, count);
                 lastItem = item;
                 count = 1;
              }
          }
      
          yield return new KeyValuePair<T, int>(lastItem, count);
      }
      

      我有兴趣看到 linq 方式。

      【讨论】:

      • 我冒昧地修复了代码中的一些错误,使其能够编译。
      【解决方案8】:

      (添加另一个答案以避免我已删除的两个赞成票计入此...)

      我对此进行了一些思考(现在我已经理解了这个问题),并且真的清楚你如何在 LINQ 中很好地做到这一点。肯定有办法做到这一点,可能使用ZipAggregate,但它们相对不清楚。使用foreach 非常简单:

      // Simplest way of building an empty list of an anonymous type...
      var results = new[] { new { Value = 0, Count = 0 } }.Take(0).ToList();
      
      // TODO: Handle empty arrays
      int currentValue = array[0];
      int currentCount = 1;
      
      foreach (var value in array.Skip(1))
      {
          if (currentValue != value)
          {
              results.Add(new { Value = currentValue, Count = currentCount });
              currentCount = 0;
              currentValue = value;
          }
          currentCount++;
      }
      // Handle tail, which we won't have emitted yet
      results.Add(new { Value = currentValue, Count = currentCount });
      

      【讨论】:

        【解决方案9】:
        var array = new int[]{};//whatever ur array is
        array.select((s)=>{return array.where((s2)=>{s == s2}).count();});
        

        唯一的问题是如果你有 1-2 次,你会得到 1-2 次的结果

        【讨论】:

          【解决方案10】:

          试试GroupByList&lt;int&gt;

                  List<int> list = new List<int>() { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
                  var res = list.GroupBy(val => val);
                  foreach (var v in res)
                  {
                      MessageBox.Show(v.Key.ToString() + "=>" + v.Count().ToString());
                  }
          

          【讨论】:

          • 你在不知情的情况下投反对票?
          • 我也设计了与上述相同的方法,所以我也认为没有必要投票
          • 我认为人们对你投了反对票,因为这不是正确的答案。他的想法是将连续次发生分组。
          猜你喜欢
          • 2013-03-15
          • 1970-01-01
          • 2019-11-14
          • 2016-01-11
          • 2013-01-11
          • 1970-01-01
          • 2016-10-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多