获取具有特定条件的第 n 个元素的索引

Get index of nth element with specific condition

假设我们有一个 List<int>,其内容类似于 [0,0,0,0,1,1,1,1,0,0,0,1,2,2,0,0,2,2],我们希望获得第 n 个不为零的数字的索引。

例如,GetNthNotZero(3) 应该 return 6.

使用 for 循环会很容易,但我觉得应该有一个 LINQ 来完成它。使用 LINQ 语句可能吗?

没有开箱即用的方法,但您是否考虑过编写自己的扩展方法来提供类似于 LINQ 的方法FindIndex()

class Program
{
    static void Main(string[] args)
    {
        var list = new List<int>{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 2, 2, 0, 0, 2, 2 };

        var index = list.FindNthIndex(x => x > 0, 3);
    }
}

public static class IEnumerableExtensions
{
    public static int FindNthIndex<T>(this IEnumerable<T> enumerable, Predicate<T> match, int count)
    {
        var index = 0;

        foreach (var item in enumerable)
        {
            if (match.Invoke(item))
                count--;
            if (count == 0)
                return index;
            index++;
        }

        return -1;
    }
}

实际上你可以用标准的 LINQ 做到这一点,你可以使用:

List<int> sequence = new List<int>{0,0,0,0,1,1,1,1,0,0,0,1,2,2,0,0,2,2};
int index = sequence.Select((x, ix) => (Item:x, Index:ix))
    .Where(x => x.Item != 0)
    .Skip(2)  // you want the 3rd, so skip 2
    .Select(x => x.Index)
    .DefaultIfEmpty(-1) // if there is no third matching condition you get -1
    .First(); // result: 6

这当然是可能的,但 Linq 方法会使它变得更加复杂。这是显式循环要好得多的情况之一。

使用 Linq 引起的两个重大并发症是:

  1. 处理空序列或没有零的序列。
  2. 正在合成要使用的索引。

Linq 解决方案可能如下所示(但请注意,使用 Linq 可能有许多不同的可能方法):

using System;
using System.Collections.Generic;
using System.Linq;

public static class Program
{
    public static void Main()
    {
        var ints = new List<int> { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 2, 2, 0, 0, 2, 2 };

        Console.WriteLine(IndexOfNthNotZero(ints, 3));                     // 6
        Console.WriteLine(IndexOfNthNotZero(Enumerable.Repeat(0, 10), 3)); // -1
        Console.WriteLine(IndexOfNthNotZero(ints, 100));                   // -1
        Console.WriteLine(IndexOfNthNotZero(Array.Empty<int>(), 0));       // -1
    }

    public static int IndexOfNthNotZero(IEnumerable<int> sequence, int n)
    {
        return sequence
            .Select((v, i) => (value:v, index:i))       // Synthesize the value and index.
            .Where(item => item.value != 0)             // Choose only the non-zero value.
            .Skip(n-1)                                  // Skip to the nth value. 
            .FirstOrDefault((value:0, index:-1)).index; // Handle missing data by supplying a default index of -1.
    }
}

请注意,此实现 returns -1 表示未找到合适的值。

将其与简单循环实现进行比较,我想您会同意使用简单循环更好!

public static int IndexOfNthNotZero(IReadOnlyList<int> sequence, int n)
{
    for (int i = 0; i < sequence.Count; ++i)
        if (sequence[i] != 0 && --n == 0) // If element matches, decrement n and return index if it reaches 0.
            return i;

    return -1;
}

或者如果您愿意(避免预递减):

public static int IndexOfNthNotZero(IReadOnlyList<int> sequence, int n)
{
    for (int i = 0, numberOfMatches = 0; i < sequence.Count; ++i)
    {
        if (sequence[i] != 0)           // If condition matches
            if (++numberOfMatches == n) // Increment number of matches, and if it reaches n
                return i;               // then return the current index
    }

    return -1;
}