Verwenden Sie Linq, um die letzten N Elemente einer Sammlung abzurufen?

284

Gibt es bei einer Sammlung eine Möglichkeit, die letzten N Elemente dieser Sammlung zu erhalten? Wenn das Framework keine Methode enthält, wie kann man dazu am besten eine Erweiterungsmethode schreiben?

Matthew Groves
quelle

Antworten:

422
collection.Skip(Math.Max(0, collection.Count() - N));

Dieser Ansatz bewahrt die Artikelreihenfolge ohne Abhängigkeit von einer Sortierung und ist weitgehend kompatibel mit mehreren LINQ-Anbietern.

Es ist wichtig, nicht Skipmit einer negativen Nummer anzurufen . Einige Anbieter, wie z. B. das Entity Framework, erzeugen eine ArgumentException, wenn ein negatives Argument angezeigt wird. Der Aufruf, Math.Maxdies ordentlich zu vermeiden.

Die folgende Klasse enthält alle wesentlichen Elemente für Erweiterungsmethoden: eine statische Klasse, eine statische Methode und die Verwendung des thisSchlüsselworts.

public static class MiscExtensions
{
    // Ex: collection.TakeLast(5);
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int N)
    {
        return source.Skip(Math.Max(0, source.Count() - N));
    }
}

Ein kurzer Hinweis zur Leistung:

Da der Aufruf von Count()eine Aufzählung bestimmter Datenstrukturen verursachen kann, besteht bei diesem Ansatz das Risiko, dass zwei Durchgänge über die Daten verursacht werden. Dies ist bei den meisten Aufzählungen kein wirkliches Problem. Tatsächlich gibt es bereits Optimierungen für Listen, Arrays und sogar EF-Abfragen, um die Count()Operation in O (1) -Zeit zu bewerten .

Wenn Sie jedoch eine Nur-Vorwärts-Aufzählung verwenden müssen und zwei Durchgänge vermeiden möchten, ziehen Sie einen Ein-Durchlauf-Algorithmus in Betracht, wie ihn Lasse V. Karlsen oder Mark Byers beschreiben. Beide Ansätze verwenden einen temporären Puffer, um Elemente während der Aufzählung zu speichern, die ausgegeben werden, sobald das Ende der Sammlung gefunden ist.

kbrimington
quelle
2
+1, da dies in Linq to Entities / SQL funktioniert. Ich vermute, dass es in Linq to Objects auch performanter ist als James Currans Strategie.
StriplingWarrior
11
Hängt von der Art der Sammlung ab. Count () könnte O (N) sein.
James Curran
3
@ James: Absolut richtig. Wenn Sie sich ausschließlich mit IEnumerable-Sammlungen befassen, kann dies eine Abfrage mit zwei Durchgängen sein. Ich wäre sehr daran interessiert, einen garantierten 1-Pass-Algorithmus zu sehen. Es könnte nützlich sein.
Kbrimington
4
Habe einige Benchmarks gemacht. Es stellt sich heraus, dass LINQ to Objects einige Optimierungen basierend auf dem von Ihnen verwendeten Auflistungstyp durchführt. Mit Arrays, Lists und LinkedLists ist James 'Lösung tendenziell schneller, wenn auch nicht um eine Größenordnung. Wenn die IEnumerable berechnet wird (z. B. über Enumerable.Range), dauert James 'Lösung länger. Ich kann mir keine Möglichkeit vorstellen, einen einzelnen Durchgang zu garantieren, ohne etwas über die Implementierung zu wissen oder Werte in eine andere Datenstruktur zu kopieren.
StriplingWarrior
1
@ RedFilter - Fair genug. Ich nehme an, meine Paging-Gewohnheiten sind hier durchgesickert. Vielen Dank für Ihr scharfes Auge.
Kbrimington
59
coll.Reverse().Take(N).Reverse().ToList();


public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> coll, int N)
{
    return coll.Reverse().Take(N).Reverse();
}

UPDATE: Um das Problem von clintp zu lösen: a) Die Verwendung der oben definierten TakeLast () -Methode löst das Problem. Wenn Sie jedoch wirklich ohne die zusätzliche Methode auskommen möchten, müssen Sie dies nur erkennen, während Enumerable.Reverse () dies kann Wenn Sie es als Erweiterungsmethode verwenden, müssen Sie es nicht folgendermaßen verwenden:

List<string> mystring = new List<string>() { "one", "two", "three" }; 
mystring = Enumerable.Reverse(mystring).Take(2).Reverse().ToList();
James Curran
quelle
Das Problem, das ich damit habe, ist, wenn ich sage: List<string> mystring = new List<string>() { "one", "two", "three" }; mystring = mystring.Reverse().Take(2).Reverse(); Ich erhalte einen Compilerfehler, weil .Reverse () void zurückgibt und der Compiler diese Methode anstelle der Linq-Methode wählt, die eine IEnumerable zurückgibt. Vorschläge?
Clinton Pierce
1
Sie können dieses Problem lösen, indem Sie mystring explizit in IEnumerable <String> umwandeln: ((IEnumerable <String>) mystring) .Reverse (). Take (2) .Reverse ()
Jan Hettich
Leicht und einfach genug, erfordert jedoch eine zweimalige vollständige Umkehrung der Reihenfolge. Dies kann der beste Weg sein
Shashwat
Ich mag es zusätzlich zu der akzeptierten Antwort von Kbrimington. Wenn Sie sich nach den letzten NDatensätzen nicht um die Bestellung kümmern , können Sie die zweite überspringen Reverse.
ZoolWay
@shashwat Es kehrt die Reihenfolge nicht zweimal "vollständig" um. Die zweite Umkehrung gilt nur für die Sammlung von N Artikeln. Abhängig davon, wie Reverse () implementiert ist, kann der erste Aufruf nur Re-Elemente umkehren. (Die .NET 4.0-Implementierung kopiert die Sammlung in ein Array und indiziert sie rückwärts))
James Curran
47

Hinweis : Ich habe Ihren Fragentitel mit der Aufschrift "Verwenden von Linq" verpasst , daher wird in meiner Antwort "Linq" nicht verwendet.

Wenn Sie vermeiden möchten, dass eine nicht verzögerte Kopie der gesamten Sammlung zwischengespeichert wird, können Sie eine einfache Methode schreiben, die dies mithilfe einer verknüpften Liste tut.

Mit der folgenden Methode wird jeder in der ursprünglichen Sammlung gefundene Wert zu einer verknüpften Liste hinzugefügt und die verknüpfte Liste auf die Anzahl der erforderlichen Elemente reduziert. Da die verknüpfte Liste während des gesamten Durchlaufens der Sammlung die ganze Zeit über auf diese Anzahl von Elementen gekürzt bleibt, wird nur eine Kopie von höchstens N Elementen aus der ursprünglichen Sammlung beibehalten.

Es ist nicht erforderlich, dass Sie die Anzahl der Elemente in der Originalsammlung kennen oder mehrmals durchlaufen.

Verwendungszweck:

IEnumerable<int> sequence = Enumerable.Range(1, 10000);
IEnumerable<int> last10 = sequence.TakeLast(10);
...

Verlängerungsmethode:

public static class Extensions
{
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> collection,
        int n)
    {
        if (collection == null)
            throw new ArgumentNullException(nameof(collection));
        if (n < 0)
            throw new ArgumentOutOfRangeException(nameof(n), $"{nameof(n)} must be 0 or greater");

        LinkedList<T> temp = new LinkedList<T>();

        foreach (var value in collection)
        {
            temp.AddLast(value);
            if (temp.Count > n)
                temp.RemoveFirst();
        }

        return temp;
    }
}
Lasse V. Karlsen
quelle
Ich denke immer noch, dass Sie eine gute, gültige Antwort haben, auch wenn es technisch nicht mit Linq verwendet wird, also gebe ich Ihnen immer noch eine +1 :)
Matthew Groves
sauber, ordentlich und erweiterbar +1!
Yasser Shaikh
1
Ich denke, es ist die einzige Lösung, die nicht dazu führt, dass der Quell-Enumerator zweimal (oder öfter) durchlaufen wird und die Materialisierung der Enumeration nicht erzwingt. Daher würde ich in den meisten Anwendungen sagen, dass dies in Bezug auf die Daten viel effizienter wäre von Gedächtnis und Geschwindigkeit.
Sprotty
30

Hier ist eine Methode, die für alle Aufzählungen funktioniert, jedoch nur temporären O (N) -Speicher verwendet:

public static class TakeLastExtension
{
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int takeCount)
    {
        if (source == null) { throw new ArgumentNullException("source"); }
        if (takeCount < 0) { throw new ArgumentOutOfRangeException("takeCount", "must not be negative"); }
        if (takeCount == 0) { yield break; }

        T[] result = new T[takeCount];
        int i = 0;

        int sourceCount = 0;
        foreach (T element in source)
        {
            result[i] = element;
            i = (i + 1) % takeCount;
            sourceCount++;
        }

        if (sourceCount < takeCount)
        {
            takeCount = sourceCount;
            i = 0;
        }

        for (int j = 0; j < takeCount; ++j)
        {
            yield return result[(i + j) % takeCount];
        }
    }
}

Verwendungszweck:

List<int> l = new List<int> {4, 6, 3, 6, 2, 5, 7};
List<int> lastElements = l.TakeLast(3).ToList();

Es verwendet einen Ringpuffer der Größe N, um die Elemente so zu speichern, wie sie angezeigt werden, und überschreibt alte Elemente mit neuen. Wenn das Ende der Aufzählung erreicht ist, enthält der Ringpuffer die letzten N Elemente.

Mark Byers
quelle
2
+1: Dies sollte eine bessere Leistung haben als meine, aber Sie sollten sicherstellen, dass es das Richtige tut, wenn die Sammlung weniger Elemente enthält als n.
Lasse V. Karlsen
Nun, die meiste Zeit gehe ich davon aus, dass die Leute beim Kopieren von Code aus SO für die Produktion vorsichtig sein werden, um solche Dinge selbst hinzuzufügen. Dies ist möglicherweise kein Problem. Wenn Sie es hinzufügen möchten, sollten Sie auch die Auflistungsvariable auf null prüfen. Ansonsten ausgezeichnete Lösung :) Ich habe überlegt, selbst einen Ringpuffer zu verwenden, da eine verknüpfte Liste den GC-Druck erhöht, aber es ist schon eine Weile her, seit ich einen gemacht habe und ich wollte mich nicht mit Testcode herumschlagen, um das herauszufinden wenn ich es richtig gemacht hätte. Ich muss sagen, dass ich mich in LINQPad verliebe
Lasse V. Karlsen
2
Eine mögliche Optimierung wäre, zu überprüfen, ob die Aufzählung IList implementiert, und die triviale Lösung zu verwenden, wenn dies der Fall ist. Der temporäre Speicheransatz wäre dann nur für das echte "Streaming" von IEnumerables erforderlich
piers7
1
trivial nit-pick: Ihre Argumente für ArgumentOutOfRangeException sind in der falschen Reihenfolge (R # sagt)
piers7
28

.NET Core 2.0+ bietet die LINQ-Methode TakeLast():

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.takelast

Beispiel :

Enumerable
    .Range(1, 10)
    .TakeLast(3) // <--- takes last 3 items
    .ToList()
    .ForEach(i => System.Console.WriteLine(i))

// outputs:
// 8
// 9
// 10
Strahl
quelle
Ich benutze: NET Standard 2.0 und ich habe es nicht verfügbar. Was ist los mit dir? :(
SuperJMN
@SuperJMN Obwohl Sie möglicherweise auf .net Standard 2.0-Bibliotheken verweisen, zielen Sie möglicherweise nicht auf die richtige Version des Dotnet-Kerns in Ihrem Projekt ab. Diese Methode ist nicht für v1.x ( netcoreapp1.x) verfügbar, sondern nur für v2.0 und v2.1 von dotnetcore ( netcoreapp2.x). Es ist möglich, dass Sie auf das gesamte Framework (z. B. net472) abzielen, das ebenfalls nicht unterstützt wird. (.net-Standardbibliotheken können von allen oben genannten verwendet werden, stellen jedoch möglicherweise nur bestimmte APIs bereit, die für ein Zielframework spezifisch sind. siehe docs.microsoft.com/en-us/dotnet/standard/frameworks )
Ray
1
Diese müssen jetzt höher sein. Keine Notwendigkeit, das Rad neu zu erfinden
James Woodley
11

Ich bin überrascht, dass niemand es erwähnt hat, aber SkipWhile hat eine Methode, die den Index des Elements verwendet .

public static IEnumerable<T> TakeLastN<T>(this IEnumerable<T> source, int n)
{
    if (source == null)
        throw new ArgumentNullException("Source cannot be null");

    int goldenIndex = source.Count() - n;
    return source.SkipWhile((val, index) => index < goldenIndex);
}

//Or if you like them one-liners (in the spirit of the current accepted answer);
//However, this is most likely impractical due to the repeated calculations
collection.SkipWhile((val, index) => index < collection.Count() - N)

Der einzige wahrnehmbare Vorteil, den diese Lösung gegenüber anderen bietet, besteht darin, dass Sie die Option haben können, ein Prädikat hinzuzufügen, um eine leistungsfähigere und effizientere LINQ-Abfrage zu erstellen, anstatt zwei separate Operationen durchzuführen, die die IEnumerable zweimal durchlaufen.

public static IEnumerable<T> FilterLastN<T>(this IEnumerable<T> source, int n, Predicate<T> pred)
{
    int goldenIndex = source.Count() - n;
    return source.SkipWhile((val, index) => index < goldenIndex && pred(val));
}
Nick Babcock
quelle
9

Verwenden Sie EnumerableEx.TakeLast in der System.Interactive-Assembly von RX. Es ist eine O (N) -Implementierung wie die von @ Mark, verwendet jedoch eine Warteschlange anstelle eines Ringpufferkonstrukts (und entfernt Elemente, wenn die Pufferkapazität erreicht ist).

(NB: Dies ist die IEnumerable-Version - nicht die IObservable-Version, obwohl die Implementierung der beiden ziemlich identisch ist.)

piers7
quelle
Dies ist die beste Antwort. Rollen Sie nicht Ihre eigenen, wenn es eine geeignete Bibliothek gibt, die die Arbeit erledigt, und das RX-Team von hoher Qualität ist.
Bradgonesurfing
Wenn Sie damit anfangen, installieren Sie es von Nuget - nuget.org/packages/Ix-Async
nikib3ro
Wird C # nicht Queue<T>mit einem Ringpuffer implementiert ?
Tigrou
@ Tigrou. Nein, es ist nicht kreisförmig
Citykid
6

Wenn Sie es mit einer Sammlung mit einem Schlüssel zu tun haben (z. B. Einträge aus einer Datenbank), wäre eine schnelle (dh schnellere als die ausgewählte Antwort) Lösung

collection.OrderByDescending(c => c.Key).Take(3).OrderBy(c => c.Key);
dav_i
quelle
+1 funktioniert für mich und es ist leicht zu lesen, ich habe eine kleine Anzahl von Objekten in meiner Liste
fubo
5

Wenn es Ihnen nichts ausmacht, als Teil der Monade in Rx einzutauchen, können Sie Folgendes verwenden TakeLast:

IEnumerable<int> source = Enumerable.Range(1, 10000);

IEnumerable<int> lastThree = source.AsObservable().TakeLast(3).AsEnumerable();
Richard Szalay
quelle
2
Sie benötigen AsObservable () nicht, wenn Sie auf System.Interactive von RX anstelle von System.Reactive verweisen (siehe meine Antwort)
piers7
2

Wenn die Verwendung einer Bibliothek eines Drittanbieters eine Option ist, definiert MoreLinq,TakeLast() welche genau dies tut.

sm
quelle
2

Ich habe versucht, Effizienz und Einfachheit zu kombinieren und am Ende Folgendes zu erreichen:

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
    if (source == null) { throw new ArgumentNullException("source"); }

    Queue<T> lastElements = new Queue<T>();
    foreach (T element in source)
    {
        lastElements.Enqueue(element);
        if (lastElements.Count > count)
        {
            lastElements.Dequeue();
        }
    }

    return lastElements;
}

Informationen zur Leistung: Wird in C # Queue<T>mithilfe eines Ringpuffers implementiert, sodass in jeder Schleife keine Objektinstanziierung durchgeführt wird (nur wenn die Warteschlange wächst). Ich habe keine Warteschlangenkapazität festgelegt (mit einem dedizierten Konstruktor), da diese Erweiterung möglicherweise von jemandem aufgerufen wird count = int.MaxValue. Für zusätzliche Leistung können Sie überprüfen, ob die Quelle implementiert ist, IList<T>und wenn ja, die letzten Werte mithilfe von Array-Indizes direkt extrahieren.

Tigrou
quelle
1

Es ist ein wenig ineffizient, das letzte N einer Sammlung mit LINQ zu nehmen, da alle oben genannten Lösungen eine Iteration über die Sammlung erfordern. TakeLast(int n)in System.Interactivehat auch dieses problem.

Wenn Sie eine Liste haben, können Sie sie effizienter mit der folgenden Methode aufteilen

/// Select from start to end exclusive of end using the same semantics
/// as python slice.
/// <param name="list"> the list to slice</param>
/// <param name="start">The starting index</param>
/// <param name="end">The ending index. The result does not include this index</param>
public static List<T> Slice<T>
(this IReadOnlyList<T> list, int start, int? end = null)
{
    if (end == null)
    {
        end = list.Count();
    }
     if (start < 0)
    {
        start = list.Count + start;
    }
     if (start >= 0 && end.Value > 0 && end.Value > start)
    {
        return list.GetRange(start, end.Value - start);
    }
     if (end < 0)
    {
        return list.GetRange(start, (list.Count() + end.Value) - start);
    }
     if (end == start)
    {
        return new List<T>();
    }
     throw new IndexOutOfRangeException(
        "count = " + list.Count() + 
        " start = " + start +
        " end = " + end);
}

mit

public static List<T> GetRange<T>( this IReadOnlyList<T> list, int index, int count )
{
    List<T> r = new List<T>(count);
    for ( int i = 0; i < count; i++ )
    {
        int j=i + index;
        if ( j >= list.Count )
        {
            break;
        }
        r.Add(list[j]);
    }
    return r;
}

und einige Testfälle

[Fact]
public void GetRange()
{
    IReadOnlyList<int> l = new List<int>() { 0, 10, 20, 30, 40, 50, 60 };
     l
        .GetRange(2, 3)
        .ShouldAllBeEquivalentTo(new[] { 20, 30, 40 });
     l
        .GetRange(5, 10)
        .ShouldAllBeEquivalentTo(new[] { 50, 60 });

}
 [Fact]
void SliceMethodShouldWork()
{
    var list = new List<int>() { 1, 3, 5, 7, 9, 11 };
    list.Slice(1, 4).ShouldBeEquivalentTo(new[] { 3, 5, 7 });
    list.Slice(1, -2).ShouldBeEquivalentTo(new[] { 3, 5, 7 });
    list.Slice(1, null).ShouldBeEquivalentTo(new[] { 3, 5, 7, 9, 11 });
    list.Slice(-2)
        .Should()
        .BeEquivalentTo(new[] {9, 11});
     list.Slice(-2,-1 )
        .Should()
        .BeEquivalentTo(new[] {9});
}
Bradgonesurfing
quelle
1

Ich weiß, dass es zu spät ist, diese Frage zu beantworten. Wenn Sie jedoch mit einer Sammlung vom Typ IList <> arbeiten und sich nicht um eine Reihenfolge der zurückgegebenen Sammlung kümmern, funktioniert diese Methode schneller. Ich habe Mark Byers Antwort verwendet und ein paar Änderungen vorgenommen. Die Methode TakeLast lautet nun:

public static IEnumerable<T> TakeLast<T>(IList<T> source, int takeCount)
{
    if (source == null) { throw new ArgumentNullException("source"); }
    if (takeCount < 0) { throw new ArgumentOutOfRangeException("takeCount", "must not be negative"); }
    if (takeCount == 0) { yield break; }

    if (source.Count > takeCount)
    {
        for (int z = source.Count - 1; takeCount > 0; z--)
        {
            takeCount--;
            yield return source[z];
        }
    }
    else
    {
        for(int i = 0; i < source.Count; i++)
        {
            yield return source[i];
        }
    }
}

Zum Test habe ich die Mark Byers-Methode und kbrimington's andswer verwendet . Dies ist Test:

IList<int> test = new List<int>();
for(int i = 0; i<1000000; i++)
{
    test.Add(i);
}

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

IList<int> result = TakeLast(test, 10).ToList();

stopwatch.Stop();

Stopwatch stopwatch1 = new Stopwatch();
stopwatch1.Start();

IList<int> result1 = TakeLast2(test, 10).ToList();

stopwatch1.Stop();

Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();

IList<int> result2 = test.Skip(Math.Max(0, test.Count - 10)).Take(10).ToList();

stopwatch2.Stop();

Und hier sind die Ergebnisse für die Aufnahme von 10 Elementen:

Geben Sie hier die Bildbeschreibung ein

und für die Aufnahme von 1000001 Elementen sind die Ergebnisse: Geben Sie hier die Bildbeschreibung ein

Sasha
quelle
1

Hier ist meine Lösung:

public static class EnumerationExtensions
{
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> input, int count)
    {
        if (count <= 0)
            yield break;

        var inputList = input as IList<T>;

        if (inputList != null)
        {
            int last = inputList.Count;
            int first = last - count;

            if (first < 0)
                first = 0;

            for (int i = first; i < last; i++)
                yield return inputList[i];
        }
        else
        {
            // Use a ring buffer. We have to enumerate the input, and we don't know in advance how many elements it will contain.
            T[] buffer = new T[count];

            int index = 0;

            count = 0;

            foreach (T item in input)
            {
                buffer[index] = item;

                index = (index + 1) % buffer.Length;
                count++;
            }

            // The index variable now points at the next buffer entry that would be filled. If the buffer isn't completely
            // full, then there are 'count' elements preceding index. If the buffer *is* full, then index is pointing at
            // the oldest entry, which is the first one to return.
            //
            // If the buffer isn't full, which means that the enumeration has fewer than 'count' elements, we'll fix up
            // 'index' to point at the first entry to return. That's easy to do; if the buffer isn't full, then the oldest
            // entry is the first one. :-)
            //
            // We'll also set 'count' to the number of elements to be returned. It only needs adjustment if we've wrapped
            // past the end of the buffer and have enumerated more than the original count value.

            if (count < buffer.Length)
                index = 0;
            else
                count = buffer.Length;

            // Return the values in the correct order.
            while (count > 0)
            {
                yield return buffer[index];

                index = (index + 1) % buffer.Length;
                count--;
            }
        }
    }

    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> input, int count)
    {
        if (count <= 0)
            return input;
        else
            return input.SkipLastIter(count);
    }

    private static IEnumerable<T> SkipLastIter<T>(this IEnumerable<T> input, int count)
    {
        var inputList = input as IList<T>;

        if (inputList != null)
        {
            int first = 0;
            int last = inputList.Count - count;

            if (last < 0)
                last = 0;

            for (int i = first; i < last; i++)
                yield return inputList[i];
        }
        else
        {
            // Aim to leave 'count' items in the queue. If the input has fewer than 'count'
            // items, then the queue won't ever fill and we return nothing.

            Queue<T> elements = new Queue<T>();

            foreach (T item in input)
            {
                elements.Enqueue(item);

                if (elements.Count > count)
                    yield return elements.Dequeue();
            }
        }
    }
}

Der Code ist etwas klobig, aber als wiederverwendbare Drop-In-Komponente sollte er in den meisten Szenarien so gut wie möglich funktionieren und den Code, der ihn verwendet, schön und präzise halten. :-)

My TakeLastfor non- IList`1basiert auf dem gleichen Ringpuffer-Algorithmus wie in den Antworten von @Mark Byers und @MackieChan weiter oben. Es ist interessant, wie ähnlich sie sind - ich habe meine völlig unabhängig geschrieben. Ich denke, es gibt wirklich nur einen Weg, einen Ringpuffer richtig zu machen. :-)

Wenn man sich die Antwort von @ kbrimington ansieht, könnte eine zusätzliche Prüfung hinzugefügt werden, um IQuerable<T>auf den Ansatz zurückzugreifen, der mit Entity Framework gut funktioniert - vorausgesetzt, das, was ich zu diesem Zeitpunkt habe, nicht.

Jonathan Gilbert
quelle
0

Unter dem realen Beispiel, wie die letzten 3 Elemente aus einer Sammlung (Array) entnommen werden:

// split address by spaces into array
string[] adrParts = adr.Split(new string[] { " " },StringSplitOptions.RemoveEmptyEntries);
// take only 3 last items in array
adrParts = adrParts.SkipWhile((value, index) => { return adrParts.Length - index > 3; }).ToArray();
Aleksey Timkov
quelle
0

Verwenden dieser Methode, um alle Bereiche ohne Fehler abzurufen

 public List<T> GetTsRate( List<T> AllT,int Index,int Count)
        {
            List<T> Ts = null;
            try
            {
                Ts = AllT.ToList().GetRange(Index, Count);
            }
            catch (Exception ex)
            {
                Ts = AllT.Skip(Index).ToList();
            }
            return Ts ;
        }
Ali asghar Fendereski
quelle
0

Wenig andere Implementierung bei Verwendung von Ringpuffer. Die Benchmarks zeigen, dass die Methode etwa doppelt so schnell ist wie die mit Queue (Implementierung von TakeLast in System.Linq ), jedoch nicht ohne Kosten - sie benötigt einen Puffer, der mit der angeforderten Anzahl von Elementen mitwächst , selbst wenn Sie eine haben kleine Sammlung können Sie große Speicherzuordnung erhalten.

public IEnumerable<T> TakeLast<T>(IEnumerable<T> source, int count)
{
    int i = 0;

    if (count < 1)
        yield break;

    if (source is IList<T> listSource)
    {
        if (listSource.Count < 1)
            yield break;

        for (i = listSource.Count < count ? 0 : listSource.Count - count; i < listSource.Count; i++)
            yield return listSource[i];

    }
    else
    {
        bool move = true;
        bool filled = false;
        T[] result = new T[count];

        using (var enumerator = source.GetEnumerator())
            while (move)
            {
                for (i = 0; (move = enumerator.MoveNext()) && i < count; i++)
                    result[i] = enumerator.Current;

                filled |= move;
            }

        if (filled)
            for (int j = i; j < count; j++)
                yield return result[j];

        for (int j = 0; j < i; j++)
            yield return result[j];

    }
}
Romasz
quelle