5

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

{ 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.

但是谷歌使用 LINQ 使用较少的代码行来使这成为可能,但没有成功。

4

10 回答 10

8

我相信做到这一点的最佳方法是使用迭代器块创建“类似 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<T>以允许自定义值的比较方式。

您可以使用这样的方法:

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
于 2012-07-04T13:45:22.097 回答
3

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

我对此进行了一些思考(现在我已经理解了这个问题),并且真的不清楚你如何在 LINQ 中很好地做到这一点。肯定有一些方法可以完成,可能使用Zipor Aggregate,但它们相对不清楚。使用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 });
于 2012-07-04T13:46:03.263 回答
3

这是一个有效的 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);

是一个证明它有效的证据。

于 2012-07-04T14:01:09.447 回答
2

这还不够短?

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 方式。

于 2012-07-04T13:49:22.917 回答
1

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

foreach(var g in numbers.GroupContiguous(i => i))
{
  Console.WriteLine("{0} => {1}", g.Key, g.Count);
}
于 2012-07-05T19:18:54.383 回答
1

看哪(你可以直接在 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),但您没有在规范中要求线性效率。

于 2012-07-06T00:59:22.720 回答
1
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());
}
于 2017-01-07T20:24:35.200 回答
0
var array = new int[]{};//whatever ur array is
array.select((s)=>{return array.where((s2)=>{s == s2}).count();});

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

于 2012-07-04T13:44:28.477 回答
0
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
于 2017-01-07T20:13:32.450 回答
-4

试穿GroupBy_List<int>

        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());
        }
于 2012-07-04T13:35:03.490 回答