C# LINQ计数整数数组中的连续重复项(int)?

C# LINQ计数整数数组中的连续重复项(int)?,c#,.net,linq,algorithm,C#,.net,Linq,Algorithm,下面是我问题的一个场景:我有一个数组,比如: { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 } 结果应该是这样的(数组元素=>其计数): 我知道这可以通过for loop实现 但谷歌希望通过使用LINQ使用更少的代码行来实现这一点,但没有成功。尝试GroupBy通过列表 List List=newlist(){4,1,1,3,3,2,5,3,2,2}; var res=list.GroupBy(val=>val); foreach(在res中的var v) { MessageB

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

{ 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }
结果应该是这样的(数组元素=>其计数):

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


但谷歌希望通过使用LINQ使用更少的代码行来实现这一点,但没有成功。

尝试
GroupBy
通过
列表

List List=newlist(){4,1,1,3,3,2,5,3,2,2};
var res=list.GroupBy(val=>val);
foreach(在res中的var v)
{
MessageBox.Show(v.Key.ToString()+“=>”+v.Count().ToString());
}

唯一的问题是,如果你有1-2次,你会得到1-2次的结果,我相信最好的方法是使用迭代器块创建一个“类似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);
    }
  }

}
对于您输入的数据,结果将是以下元组:

4 1 1 2 3 2 2 1 5 1 3 1 2 2 4 1 1 2 3 2 2 1 5 1 3 1 2 2 (添加另一个答案,以避免我删除的一个答案中的两张向上的选票计入此…)

我对此有过一些思考(现在我已经理解了这个问题),现在还不清楚在LINQ中你会如何很好地做到这一点。确实有一些方法可以做到这一点,可能使用
Zip
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 });
这个不够短

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);
}
公共静态IEnumerable重复(
这是(不可数的来源)
{
整数计数=0;
T lastItem=source.First();
foreach(源中的var项)
{
if(等于(项目,最后一个项目))
{
计数++;
}
其他的
{
返回新的KeyValuePair(lastItem,count);
lastItem=项目;
计数=1;
}
}
返回新的KeyValuePair(lastItem,count);
}

我很想看看linq方法。

这里有一个linq表达式可以工作(编辑:代码再收紧一点):


这表明它是有效的。

我已经编写了您需要的方法。这是怎么称呼它的

foreach(var g in numbers.GroupContiguous(i => i))
{
  Console.WriteLine("{0} => {1}", g.Key, g.Count);
}
请注意(您可以直接在LINQPad中运行此操作--
rle
就是魔法发生的地方):

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

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

如果示例为4、1、1、3、3、2、5、3、2、2,很难看到数组长度如何固定为6…现在请检查以下问题:为什么输出具有数字“2”和“3”的重复键?难道你不应该期望一个输出是“4=>1,1=>2,3=>3,2=>3,5=>1”?你真的在寻找吗?@sandeep:这不是真的“当然”——你的问题不清楚,因为你从来没有使用“连续”这个词,这是这里最重要的一点。三个人都回答了这个问题,假设你的意思是只计算元素的出现次数,这表明你应该进行编辑以使其更清楚…y不知道什么就投否决票?我也设计了同样的上述方法,所以我也认为没有必要投否决票。我认为人们投否决票是因为这不是正确的答案。他的想法是对连续发生的事件进行分组。使用
GetEnumerator
是否有优势,或者只是为了避免按要求使用
foreach
。使用枚举器允许我这样做,但仍然只检索和检查每个元素一次。我冒昧地修复了代码中的一些错误,允许它编译。在某种程度上,这是正确的答案,因为它使用了问题中所述的LINQ。出于对这种方法的效率的好奇,我计算了创建的枚举数和给定10个元素的输入数组访问的项目数。创建了17个枚举数,访问了101个元素,而最理想的方法是创建1个枚举数,访问10个元素。欢迎使用堆栈溢出!花一分钟的时间来阅读-这看起来很有帮助,但是它会从代码的解释中受益,考虑到这一点。最好用代码来包含一些上下文/解释,因为这使得答案对OP和未来读者更有用。
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);
}
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);
foreach(var g in numbers.GroupContiguous(i => i))
{
  Console.WriteLine("{0} => {1}", g.Key, g.Count);
}
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);
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
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());
}