Ich verwende .NET 3.5 und möchte jedes * n
* -te Element aus einer Liste erhalten. Es ist mir egal, ob dies mit einem Lambda-Ausdruck oder LINQ erreicht wird.
Bearbeiten
Sieht so aus, als hätte diese Frage eine Menge Debatten ausgelöst (was eine gute Sache ist, richtig?). Die Hauptsache, die ich gelernt habe, ist, dass Sie, wenn Sie glauben, dass Sie alle Möglichkeiten kennen, etwas zu tun (auch so einfach wie das), erneut nachdenken!
return list.Where((x, i) => i % nStep == 0);
Ich weiß, dass es "old school" ist, aber warum nicht einfach eine for-Schleife mit Stepping = n verwenden?
Hört sich an wie
IEnumerator<T> GetNth<T>(List<T> list, int n) {
for (int i=0; i<list.Count; i+=n)
yield return list[i]
}
würde den Trick tun. Ich sehe keine Notwendigkeit, Linq oder einen Lambda-Ausdruck zu verwenden.
BEARBEITEN:
Mach es
public static class MyListExtensions {
public static IEnumerable<T> GetNth<T>(this List<T> list, int n) {
for (int i=0; i<list.Count; i+=n)
yield return list[i];
}
}
und Sie schreiben auf LINQische Weise
from var element in MyList.GetNth(10) select element;
2. Edit :
Um es noch mehr LINQish zu machen
from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];
Sie können die Where-Überladung verwenden, die den Index zusammen mit dem Element übergibt
var everyFourth = list.Where((x,i) => i % 4 == 0);
Für Schleife
for(int i = 0; i < list.Count; i += n)
//Nth Item..
Ich bin nicht sicher, ob ein LINQ-Ausdruck möglich ist, aber ich weiß, dass Sie die Erweiterungsmethode Where
verwenden können. Zum Beispiel, um jeden fünften Artikel zu erhalten:
List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList();
Dies wird den ersten Artikel und jeden fünften von dort erhalten. Wenn Sie beim fünften Element anstelle des ersten beginnen möchten, vergleichen Sie mit 4 und nicht mit 0.
Ich denke, wenn Sie eine linq-Erweiterung bereitstellen, sollten Sie in der Lage sein, auf der am wenigsten spezifischen Schnittstelle, also auf IEnumerable, zu arbeiten. Wenn Sie die Geschwindigkeit besonders für große N wollen, können Sie den indizierten Zugriff natürlich überladen. Letzteres macht die Iteration über große Mengen nicht benötigter Daten überflüssig und ist viel schneller als die Where-Klausel. Durch die Bereitstellung beider Überladungen kann der Compiler die am besten geeignete Variante auswählen.
public static class LinqExtensions
{
public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
{
if (n < 0)
throw new ArgumentOutOfRangeException("n");
if (n > 0)
{
int c = 0;
foreach (var e in list)
{
if (c % n == 0)
yield return e;
c++;
}
}
}
public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
{
if (n < 0)
throw new ArgumentOutOfRangeException("n");
if (n > 0)
for (int c = 0; c < list.Count; c += n)
yield return list[c];
}
}
Imho ist keine Antwort richtig. Alle Lösungen beginnen bei 0. Aber ich möchte das echte n-te Element haben
public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
{
for (int i = n - 1; i < list.Count; i += n)
yield return list[i];
}
@belucha Das gefällt mir, weil der Client-Code sehr gut lesbar ist und der Compiler die effizienteste Implementierung auswählt. Darauf würde ich aufbauen, indem ich die Anforderungen auf IReadOnlyList<T>
reduziere und die Division für Hochleistungs-LINQ speichere:
public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) {
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
int i = n;
foreach (var e in list) {
if (++i < n) { //save Division
continue;
}
i = 0;
yield return e;
}
}
public static IEnumerable<T> GetNth<T>(this IReadOnlyList<T> list, int n
, int offset = 0) { //use IReadOnlyList<T>
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
for (var i = offset; i < list.Count; i += n) {
yield return list[i];
}
}