एक संग्रह के अंतिम एन तत्वों को प्राप्त करने के लिए Linq का उपयोग करना?


284

एक संग्रह को देखते हुए, क्या उस संग्रह के अंतिम एन तत्वों को प्राप्त करने का एक तरीका है? यदि फ्रेमवर्क में कोई विधि नहीं है, तो ऐसा करने के लिए एक्सटेंशन विधि लिखने का सबसे अच्छा तरीका क्या होगा?

जवाबों:


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

यह दृष्टिकोण किसी भी छँटाई पर निर्भरता के बिना आइटम ऑर्डर को संरक्षित करता है, और कई LINQ प्रदाताओं में व्यापक संगतता है।

यह ध्यान रखना महत्वपूर्ण है कि Skipनकारात्मक संख्या के साथ कॉल न करें । कुछ प्रदाता, जैसे कि एंटिटी फ्रेमवर्क, एक नकारात्मक तर्क के साथ प्रस्तुत किए जाने पर एक ArgumentException उत्पन्न करेगा। Math.Maxबड़े करीने से इस कॉल से बचा जाता है।

नीचे दिए गए वर्ग में विस्तार विधियों के लिए सभी आवश्यक हैं, जो हैं: एक स्थिर वर्ग, एक स्थिर विधि और thisकीवर्ड का उपयोग ।

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));
    }
}

प्रदर्शन पर एक संक्षिप्त नोट:

क्योंकि कॉल Count()कुछ डेटा संरचनाओं की गणना का कारण बन सकती है, इस दृष्टिकोण से डेटा पर दो पास होने का जोखिम होता है। यह वास्तव में सबसे enumerables के साथ एक समस्या नहीं है; वास्तव में, Count()ओ (1) समय में ऑपरेशन का मूल्यांकन करने के लिए सूची, एरे, और यहां तक ​​कि ईएफ प्रश्नों के लिए अनुकूलन पहले से मौजूद हैं ।

यदि, हालांकि, आपको केवल-फॉरवर्ड-एन्यूमरेबल का उपयोग करना चाहिए और दो पास बनाने से बचना चाहते हैं, तो एक पास-पास एल्गोरिथ्म पर विचार करें, जैसे कि लास वी। कार्लसन या मार्क बायर्स वर्णन करते हैं। इन दोनों दृष्टिकोणों ने गणना करते समय वस्तुओं को धारण करने के लिए एक अस्थायी बफर का उपयोग किया है, जो संग्रह के अंत में मिल जाने के बाद उपजते हैं।


2
+1, जैसा कि Linq में Entities / SQL में काम करता है। मैं अनुमान लगा रहा हूं कि यह जेम्स क्यूरन की रणनीति की तुलना में लिनक में अधिक प्रदर्शन करने वाला है।
स्ट्रिपिंगवर्यर 20

11
संग्रह की प्रकृति पर निर्भर करता है। गणना () ओ (एन) हो सकती है।
जेम्स कुरेन

3
@ नाम: बिल्कुल सही। यदि IEnumerable संग्रह के साथ कड़ाई से व्यवहार किया जाता है, तो यह दो-पास क्वेरी हो सकता है। मुझे गारंटीकृत 1-पास एल्गोरिथम देखने में बहुत दिलचस्पी होगी। यह उपयोगी हो सकता है।
kbrimington

4
कुछ बेंचमार्क किए। यह LINQ को ऑब्जेक्ट में बदल देता है जो आपके द्वारा उपयोग किए जा रहे संग्रह के प्रकार के आधार पर कुछ अनुकूलन करता है। सरणियों, Listएस और LinkedListएस का उपयोग करते हुए , जेम्स का समाधान तेज हो जाता है, हालांकि परिमाण के क्रम से नहीं। यदि IEnumerable की गणना की जाती है (Enumerable.Range के माध्यम से, उदाहरण के लिए), जेम्स का समाधान अधिक समय लेता है। किसी भी डेटा संरचना के कार्यान्वयन या मूल्यों को कॉपी करने के बारे में कुछ जाने बिना किसी भी पास की गारंटी देने के किसी भी तरीके के बारे में मैं नहीं सोच सकता।
स्ट्रिपिंगवर्यर

1
@ राफिल्टर - पर्याप्त रूप से उचित। मुझे लगता है कि मेरी पेजिंग की आदतें यहां से लीक हो गई हैं। आपकी गहरी नजर के लिए धन्यवाद।
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();
}

अद्यतन: क्लिंट की समस्या का समाधान करने के लिए: ए) लेवलस्ट () विधि का उपयोग करके मैंने समस्या को हल किया है, लेकिन यदि आप वास्तव में इसे अतिरिक्त विधि के बिना करना चाहते हैं, तो आपको बस यह पहचानना होगा कि Enumerable.Reterse () हो सकता है एक विस्तार विधि के रूप में उपयोग किया जाता है, आपको इसका उपयोग करने की आवश्यकता नहीं है:

List<string> mystring = new List<string>() { "one", "two", "three" }; 
mystring = Enumerable.Reverse(mystring).Take(2).Reverse().ToList();

मेरे पास जो समस्या है, वह यह है कि अगर मैं कहता हूं: List<string> mystring = new List<string>() { "one", "two", "three" }; mystring = mystring.Reverse().Take(2).Reverse(); मुझे एक संकलक त्रुटि मिलती है क्योंकि .Reverse () शून्य देता है और संकलक लाइनक के बजाय उस विधि को चुनता है जो IEnumerable देता है। सुझाव?
क्लिंटन पियर्स

1
आप IEnumerable <String>: (IEnumerable <String>) मिस्ट्रिंग) को स्पष्ट रूप से कास्टिंग करके इस समस्या को हल कर सकते हैं ।Rerseerse ()। Take (2) .Reverse ()
Jan

आसान और सरल लेकिन पूरी तरह से दो बार आदेश को उलटने की आवश्यकता होती है। यह सबसे अच्छा तरीका हो सकता है
shashwat

मुझे यह सब पसंद है, इसके अलावा मुझे इसका जवाब देना है। यदि आपके पास पिछले Nरिकॉर्ड रखने के बाद आदेश की परवाह नहीं है तो आप दूसरे को छोड़ सकते हैं Reverse
ज़ूलवे

@ शाश्वत यह दो बार "पूरी तरह से" आदेश को उलट नहीं करता है। दूसरा उलटा केवल एन आइटम के संग्रह पर लागू होता है। इसके अलावा, इस बात पर निर्भर करता है कि रिवर्स कॉल को कैसे लागू किया जाता है, यह केवल एन आइटम को उलट सकता है। (.NET 4.0 कार्यान्वयन संग्रह को एक सरणी में कॉपी करेगा, और इसे पीछे से अनुक्रमणित करेगा)
जेम्स क्यूरन

47

नोट : मैंने आपका प्रश्न शीर्षक याद किया जिसमें कहा गया था कि Linq का उपयोग करना , इसलिए मेरा जवाब वास्तव में Linq का उपयोग नहीं करता है।

यदि आप पूरे संग्रह की गैर-आलसी प्रतिलिपि को कैशिंग से बचना चाहते हैं, तो आप एक सरल विधि लिख सकते हैं जो लिंक की गई सूची का उपयोग करके करती है।

निम्न विधि मूल संग्रह में प्रत्येक मूल्य को एक लिंक की गई सूची में जोड़ देगी, और आवश्यक सूची को लिंक करके नीचे आवश्यक वस्तुओं की संख्या को ट्रिम कर देगी। चूँकि यह पूरी सूची में संग्रह के माध्यम से पूरे समय आइटम्स की छंटनी की गई लिंक की सूची को बनाए रखता है, यह केवल मूल संग्रह से अधिकांश एन आइटमों की एक प्रति रखेगा।

आपको मूल संग्रह में वस्तुओं की संख्या जानने की आवश्यकता नहीं है, और न ही एक से अधिक बार पुनरावृति।

उपयोग:

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

विस्तार विधि:

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;
    }
}

मुझे अभी भी लगता है कि आपके पास एक अच्छा, वैध उत्तर है, भले ही यह तकनीकी रूप से Linq का उपयोग नहीं कर रहा है, इसलिए मैं अभी भी आपको एक +1 देता हूं
मैथ्यू ग्रूव्स

साफ, स्वच्छ और एक्स्टेंसिबल +1!
यासिर शेख

1
मुझे लगता है कि इसका एकमात्र समाधान है जो स्रोत एन्यूमरेटर को दो बार (या अधिक) से चलाने का कारण नहीं बनता है, और एन्यूमरेशन के भौतिककरण को मजबूर नहीं करता है, इसलिए अधिकांश अनुप्रयोगों में मैं कहूंगा कि यह बहुत अधिक कुशल होगा स्मृति और गति की।
स्पिरेटी

30

यहाँ एक विधि है जो किसी भी गणना करने योग्य काम करती है लेकिन केवल O (N) अस्थायी संग्रहण का उपयोग करती है:

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];
        }
    }
}

उपयोग:

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

यह तत्वों को संग्रहीत करने के लिए आकार N के रिंग बफर का उपयोग करके काम करता है क्योंकि यह उन्हें देखता है, नए तत्वों के साथ पुराने तत्वों को अधिलेखित करता है। जब एन्यूमरेबल का अंत रिंग रिंग तक पहुंच जाता है, तो अंतिम एन तत्व होते हैं।


2
+1: इसमें मेरा प्रदर्शन बेहतर होना चाहिए, लेकिन आपको यह सुनिश्चित करना चाहिए कि जब संग्रह में कम तत्व हों तो यह सही काम करे n
लास वी। कार्लसन 20

खैर, ज्यादातर समय मुझे लगता है कि जब लोग खुद को ऐसी चीजों को जोड़ने के लिए उत्पादन उपयोग के लिए एसओ से कोड की नकल करते हैं तो लोग ध्यान रखेंगे, यह एक समस्या नहीं हो सकती है। यदि आप इसे जोड़ने जा रहे हैं, तो साथ ही अशक्त के लिए संग्रह चर की जाँच करने पर विचार करें। अन्यथा, उत्कृष्ट समाधान :) मैं खुद को रिंग-बफर का उपयोग करने पर विचार कर रहा था, क्योंकि एक लिंक की गई सूची जीसी-दबाव को जोड़ देगी, लेकिन यह एक समय हो गया है क्योंकि मैंने एक किया था और मुझे यह पता लगाने के लिए परीक्षण-कोड के साथ परेशानी नहीं करनी थी अगर मैंने इसे सही किया। मुझे कहना होगा कि मैं LINQPad के साथ प्यार में पड़ रहा हूँ, हालांकि :) linqpad.net
Lasse V. Karlsen

2
एक संभव अनुकूलन यह जांचने के लिए होगा कि क्या गणना योग्य आईलिस्ट ने कार्यान्वित किया है, और यदि यह करता है तो तुच्छ समाधान का उपयोग करें। अस्थायी भंडारण के दृष्टिकोण को तब केवल 'स्ट्रीमिंग' IEnumerables के लिए आवश्यक होगा
piers7

1
तुच्छ नाइट-पिक: ArgumentOutOfRangeException पर आपके तर्क गलत क्रम में हैं (R # कहते हैं)
piers7

28

.NET कोर 2.0+ LINQ विधि प्रदान करता है TakeLast():

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

उदाहरण :

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

// outputs:
// 8
// 9
// 10

मैं उपयोग कर रहा हूँ: NET मानक 2.0 और मेरे पास उपलब्ध नहीं है। क्या गलत है? :(
सुपर जेएमएन

@SuperJMN भले ही आप .net मानक 2.0 पुस्तकालयों का संदर्भ ले रहे हों, लेकिन आप अपने प्रोजेक्ट में डॉटनेट कोर के सही संस्करण को लक्षित नहीं कर सकते हैं। यह विधि v1.x ( netcoreapp1.x) के लिए उपलब्ध नहीं है, लेकिन केवल डॉटनेट के v2.0 और v2.1 ( netcoreapp2.x) के लिए है। यह संभव है कि आप शायद पूर्ण रूपरेखा (जैसे net472) को लक्षित कर रहे हैं जो कि असमर्थित भी है। (.net मानक का उपयोग उपर्युक्त में से किसी के द्वारा भी किया जा सकता है, लेकिन केवल कुछ एपीआई को लक्ष्य लेआउट के लिए विशिष्ट रूप से उजागर कर सकता है। डॉक्स .microsoft.com/en-us/dotnet/standard/ frameworks देखें )
रे

1
इन्हें अभी और ऊंचा करने की जरूरत है। पहिए का फिर से आविष्कार करने की आवश्यकता नहीं है
जेम्स वुडली

11

मुझे आश्चर्य है कि किसी ने भी इसका उल्लेख नहीं किया है, लेकिन SkipWhile में एक विधि है जो तत्व के सूचकांक का उपयोग करती है

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)

एकमात्र समाधान जो दूसरों के ऊपर प्रस्तुत करता है, वह यह है कि आपके पास दो अलग-अलग ऑपरेशन होने के बजाय एक अधिक शक्तिशाली और कुशल LINQ क्वेरी बनाने के लिए एक विधेय में जोड़ने का विकल्प हो सकता है, जो IEnumerable को दो बार पार करता है।

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));
}

9

RX के System.Interactive असेंबली में EnumerableEx.TakeLast का उपयोग करें। यह @ मार्क की तरह एक ओ (एन) कार्यान्वयन है, लेकिन यह रिंग-बफर निर्माण (और बफर क्षमता तक पहुंचने पर आइटम को ख़राब करने) के बजाय एक कतार का उपयोग करता है।

(NB: यह IEnumerable संस्करण है - IObservable संस्करण नहीं, हालांकि दोनों का कार्यान्वयन बहुत अधिक है)


यह सबसे अच्छा जवाब है। अगर कोई उपयुक्त पुस्तकालय है जो नौकरी करता है और RX टीम उच्च गुणवत्ता वाली है तो अपना रोल मत करो।
ब्रैडगोनसर्फिंग

यदि आप इसके साथ जा रहे हैं, तो इसे Nuget - nuget.org/packages/Ix-Async
nikib3ro

क्या C # Queue<T>एक परिपत्र बफर का उपयोग करके लागू नहीं किया गया है ?
टाइगरो

@ टटोलना। नहीं, यह परिपत्र नहीं है
शहरकीड


6

यदि आप एक कुंजी के साथ एक संग्रह के साथ काम कर रहे हैं (जैसे डेटाबेस से प्रविष्टियाँ) एक त्वरित (यानी चयनित उत्तर की तुलना में तेज़) समाधान होगा

collection.OrderByDescending(c => c.Key).Take(3).OrderBy(c => c.Key);

+1 मेरे लिए काम करता है और इसे पढ़ना आसान है, मेरी सूची में कम संख्या में वस्तुएं हैं
fubo

5

यदि आप मोन के भाग के रूप में Rx में डुबकी लगाने से गुरेज नहीं करते हैं, तो आप इसका उपयोग कर सकते हैं TakeLast:

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

IEnumerable<int> lastThree = source.AsObservable().TakeLast(3).AsEnumerable();

2
यदि आपको RX के System.Interactive के बजाय System.Reactive (मेरे जवाब देखें) के संदर्भ में AsObservable () की आवश्यकता नहीं है
piers7

2

यदि थर्ड-पार्टी लाइब्रेरी का उपयोग करना एक विकल्प है, तो MoreLinq परिभाषित करता है TakeLast()जो वास्तव में ऐसा करता है।


2

मैंने दक्षता और सादगी को संयोजित करने और इसके साथ अंत करने की कोशिश की:

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;
}

प्रदर्शन के बारे में: सी # में, Queue<T>एक परिपत्र बफर का उपयोग करके लागू किया जाता है ताकि प्रत्येक लूप में कोई वस्तु तात्कालिकता न हो (केवल जब कतार बढ़ रही हो)। मैंने कतार क्षमता (समर्पित कंस्ट्रक्टर का उपयोग करके) सेट नहीं की क्योंकि कोई इस एक्सटेंशन को कॉल कर सकता है count = int.MaxValue। अतिरिक्त प्रदर्शन के लिए आप जाँच सकते हैं कि क्या स्रोत लागू होता है IList<T>और यदि हाँ, तो सीधे सरणी इंडेक्स का उपयोग करके अंतिम मान निकालें।


1

यह LINQ का उपयोग कर एक संग्रह के अंतिम एन लेने के लिए थोड़ा अक्षम है क्योंकि उपरोक्त सभी समाधानों को संग्रह में पुनरावृत्ति की आवश्यकता होती है। TakeLast(int n)में System.Interactiveभी यह समस्या है।

यदि आपके पास एक और अधिक कुशल चीज़ की सूची है, तो निम्न विधि का उपयोग करके इसे स्लाइस करें

/// 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);
}

साथ में

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;
}

और कुछ परीक्षण मामलों

[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});
}

1

मुझे पता है कि इस प्रश्न का उत्तर देने में देर हो चुकी है। लेकिन अगर आप IList <> के संग्रह के साथ काम कर रहे हैं और आप लौटे हुए संग्रह के ऑर्डर की परवाह नहीं करते हैं, तो यह विधि तेजी से काम कर रही है। मैंने मार्क बायर्स उत्तर का उपयोग किया है और थोड़ा बदलाव किया है। तो अब विधि TakeLast है:

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];
        }
    }
}

परीक्षण के लिए मैंने मार्क बायर्स विधि और क्रेमिंगटन की और वाइसर का उपयोग किया है । यह परीक्षण है:

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();

और यहाँ 10 तत्व लेने के लिए परिणाम हैं:

यहां छवि विवरण दर्ज करें

और 1000001 तत्व परिणाम लेने के लिए हैं: यहां छवि विवरण दर्ज करें


1

यहाँ मेरा समाधान है:

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();
            }
        }
    }
}

कोड थोड़ा चंकी है, लेकिन ड्रॉप-इन पुन: प्रयोज्य घटक के रूप में, इसे अधिकांश परिदृश्यों में भी प्रदर्शन करना चाहिए, और यह उस कोड को रखेगा जो इसे अच्छा और संक्षिप्त उपयोग कर रहा है। :-)

मेरी TakeLastगैर के लिएIList`1 लिए एक ही रिंग बफर एल्गोरिथ्म पर आधारित है जो @Mark बायर्स और @MackieChan द्वारा उत्तर में दिया गया है। यह दिलचस्प है कि वे कितने समान हैं - मैंने पूरी तरह से स्वतंत्र रूप से मेरा लिखा। लगता है कि वास्तव में रिंग बफर को ठीक से करने का सिर्फ एक तरीका है। :-)

@ Kbrimington के जवाब को देखते हुए, इस IQuerable<T>दृष्टिकोण में वापस आने के लिए एक अतिरिक्त जांच जोड़ी जा सकती है जो एंटिटी फ्रेमवर्क के साथ अच्छी तरह से काम करती है - यह मानते हुए कि मेरे पास इस बिंदु पर क्या है।


0

एक संग्रह (सरणी) से अंतिम 3 तत्वों को लेने के वास्तविक उदाहरण के नीचे:

// 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();

0

इस विधि का उपयोग त्रुटि के बिना सभी रेंज प्राप्त करने के लिए

 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 ;
        }

0

परिपत्र बफर के उपयोग के साथ थोड़ा अलग कार्यान्वयन। मानक चलता है कि विधि के लगभग दो बार का उपयोग कर लोगों की तुलना में तेजी है कतार (के कार्यान्वयन TakeLast में System.Linq ,) हालांकि नहीं एक लागत के बिना - यह एक बफर जो तत्वों का अनुरोध किया संख्या के साथ साथ बढ़ता है की जरूरत है, भले ही आप एक है छोटे संग्रह से आपको विशाल मेमोरी आवंटन प्राप्त हो सकता है।

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];

    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.