Digamos que tenemos una List<int> con contenido como [0,0,0,0,1,1,1,1,0,0,0,1,2,2,0,0,2,2] y queremos tener el índice del enésimo número que no es cero.
Por ejemplo, GetNthNotZero(3) debería devolver 6.
Sería fácil con un bucle for, pero creo que debería haber un LINQ para lograrlo. ¿Es eso posible con una declaración LINQ?
No hay un método listo para usar, pero ¿ha considerado escribir su propio método de extensión para proporcionar algo similar al FindIndex() de LINQ?
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; } }En realidad, puede hacer eso con LINQ estándar, puede usar:
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: 6Esto es ciertamente posible, pero el enfoque de Linq lo hará mucho más complicado. Este es uno de esos casos en los que un bucle explícito es mucho mejor.
Dos complicaciones significativas que surgen del uso de Linq son:
Una solución de Linq podría verse así (pero tenga en cuenta que probablemente haya muchos enfoques posibles diferentes usando 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. } } Tenga en cuenta que esta implementación devuelve -1 para indicar que no se encontró un valor adecuado.
Compare eso con una implementación de bucle simple y creo que estará de acuerdo en que es mejor usar un bucle simple.
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; }O alternativamente si lo prefiere (evitando el predecremento):
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; }