LINQ के साथ Sublists में स्प्लिट लिस्ट


377

क्या कोई तरीका है जिससे मैं अलग हो सकता हूं List<SomeObject>SomeObject प्रत्येक विभाजन के परिसीमन के रूप में आइटम इंडेक्स का उपयोग करते हुए, कई अलग-अलग सूचियों में ?

मुझे उदाहरण दें:

मैं एक है List<SomeObject>और मैं जरूरत है एक List<List<SomeObject>>याList<SomeObject>[] तो यह है कि इन जिसके परिणामस्वरूप सूचियों में से प्रत्येक के मूल सूची (क्रमिक रूप से) के 3 आइटम के एक समूह में शामिल होंगे।

उदा .:

  • मूल सूची: [a, g, e, w, p, s, q, f, x, y, i, m, c]

  • परिणाम सूची: [a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]

मुझे इस फ़ंक्शन के पैरामीटर होने के लिए परिणामी सूचियों के आकार की भी आवश्यकता होगी।

जवाबों:


378

निम्नलिखित कोड का प्रयास करें।

public static IList<IList<T>> Split<T>(IList<T> source)
{
    return  source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / 3)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}

सूचकांक द्वारा तत्वों को पहले समूह में लाने का विचार है। तीन से विभाजित उन्हें 3. के समूहों में समूहीकरण फिर एक सूची में प्रत्येक समूह में परिवर्तित करने का प्रभाव और है IEnumerableकी Listएक करने के लिए Listकी Listरों


21
GroupBy एक अंतर्निहित सॉर्ट करता है। वह प्रदर्शन को मार सकता है। हमें जो कुछ चाहिए वह SelectMany के विलोम है।
येलफेल्डम

5
@Justice, GroupBy हैशिंग द्वारा लागू किया जा सकता है। आप कैसे जानते हैं कि GroupBy का कार्यान्वयन "प्रदर्शन को मार सकता है"?
एमी बी

5
GroupBy कुछ भी वापस नहीं करता है जब तक कि वह सभी तत्वों की गणना नहीं करता है। इसलिए यह धीमी है। ओपी चाहता है कि सूचियां सन्निहित हैं, इसलिए एक बेहतर विधि [a,g,e]मूल सूची के किसी भी अधिक गणना करने से पहले पहली सबलिस्ट प्राप्त कर सकती है ।
कर्नल पैनिक

9
एक अनंत IEnumerable का चरम उदाहरण लें। GroupBy(x=>f(x)).First()एक समूह कभी नहीं होगा। ओपी ने सूचियों के बारे में पूछा, लेकिन अगर हम IEnumerable के साथ काम करने के लिए लिखते हैं, तो केवल एक पुनरावृत्ति बनाते हुए, हम प्रदर्शन लाभ प्राप्त करते हैं।
कर्नल पैनिक

8
@ निक आदेश हालांकि अपने तरीके से संरक्षित नहीं है। यह जानना अभी भी एक अच्छी बात है लेकिन आप उन्हें (0,3,6,9, ...), (1,4,7,10, ...) में विभाजित करेंगे, (2,5,8) ,1 1,...)। यदि आदेश मायने नहीं रखता है तो यह ठीक है लेकिन इस मामले में ऐसा लगता है जैसे यह मायने रखता है।
रिएफेक्सस

325

यह सवाल थोड़ा पुराना है, लेकिन मैंने अभी यह लिखा है, और मुझे लगता है कि यह अन्य प्रस्तावित समाधानों की तुलना में थोड़ा अधिक सुरुचिपूर्ण है:

/// <summary>
/// Break a list of items into chunks of a specific size
/// </summary>
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunksize)
{
    while (source.Any())
    {
        yield return source.Take(chunksize);
        source = source.Skip(chunksize);
    }
}

14
इस घोल से प्यार करें। मैं एक अनंत लूप को रोकने के लिए इस पवित्रता जांच को जोड़ने की सलाह if (chunksize <= 0) throw new ArgumentException("Chunk size must be greater than zero.", "chunksize");
दूंगा

10
मुझे यह पसंद है, लेकिन यह सुपर कुशल नहीं है
सैम केसर

51
मुझे यह पसंद है लेकिन समय दक्षता है O(n²)। आप सूची के माध्यम से पुनरावृत्ति कर सकते हैं और एक O(n)समय प्राप्त कर सकते हैं।
hIpPy

8
@hppy, यह कैसे है n ^ 2? मुझे रैखिक लगता है
विवेक महाराज

13
@vivekmaharajh sourceको IEnumerableहर बार लपेटा जाता है। इसलिए तत्वों को लेने से s की sourceपरतें Skip
गुजरती हैं

99

सामान्य तौर पर केसीबी द्वारा सुझाया गया दृष्टिकोण ठीक काम करता है, वास्तव में यदि आप इसमें पास हो रहे हैं तो List<T>इसे गलती करना मुश्किल है, शायद मैं इसमें बदलाव करूंगा:

public static IEnumerable<IEnumerable<T>> ChunkTrivialBetter<T>(this IEnumerable<T> source, int chunksize)
{
   var pos = 0; 
   while (source.Skip(pos).Any())
   {
      yield return source.Skip(pos).Take(chunksize);
      pos += chunksize;
   }
}

जो बड़े पैमाने पर कॉल चेन से बचेंगी। बहरहाल, इस दृष्टिकोण में एक सामान्य दोष है। यह चल रहे प्रयास को हाइलाइट करने के लिए, प्रति chunk प्रति दो enumerations को उत्प्रेरित करता है:

foreach (var item in Enumerable.Range(1, int.MaxValue).Chunk(8).Skip(100000).First())
{
   Console.WriteLine(item);
}
// wait forever 

इसे दूर करने के लिए हम कैमरन के दृष्टिकोण की कोशिश कर सकते हैं , जो उपरोक्त परीक्षणों को उड़ते हुए रंगों में बदल देता है क्योंकि यह केवल एक बार गणना करता है।

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

यह बताने के लिए कि दौड़ने की कोशिश करें:

foreach (var item in Enumerable.Range(1, int.MaxValue)
               .Select(x => x + new string('x', 100000))
               .Clump(10000).Skip(100).First())
{
   Console.Write('.');
}
// OutOfMemoryException

अंत में, किसी भी कार्यान्वयन को उदाहरणों के क्रम से बाहर निकालने में सक्षम होना चाहिए, उदाहरण के लिए:

Enumerable.Range(1,3).Chunk(2).Reverse().ToArray()
// should return [3],[1,2]

इस उत्तर के मेरे पहले संशोधन जैसे कई अत्यधिक इष्टतम समाधान वहां विफल रहे। इसी मुद्दे को कैस्परऑन के अनुकूलित उत्तर में देखा जा सकता है ।

इन सभी समस्याओं के समाधान के लिए आप निम्नलिखित का उपयोग कर सकते हैं:

namespace ChunkedEnumerator
{
    public static class Extensions 
    {
        class ChunkedEnumerable<T> : IEnumerable<T>
        {
            class ChildEnumerator : IEnumerator<T>
            {
                ChunkedEnumerable<T> parent;
                int position;
                bool done = false;
                T current;


                public ChildEnumerator(ChunkedEnumerable<T> parent)
                {
                    this.parent = parent;
                    position = -1;
                    parent.wrapper.AddRef();
                }

                public T Current
                {
                    get
                    {
                        if (position == -1 || done)
                        {
                            throw new InvalidOperationException();
                        }
                        return current;

                    }
                }

                public void Dispose()
                {
                    if (!done)
                    {
                        done = true;
                        parent.wrapper.RemoveRef();
                    }
                }

                object System.Collections.IEnumerator.Current
                {
                    get { return Current; }
                }

                public bool MoveNext()
                {
                    position++;

                    if (position + 1 > parent.chunkSize)
                    {
                        done = true;
                    }

                    if (!done)
                    {
                        done = !parent.wrapper.Get(position + parent.start, out current);
                    }

                    return !done;

                }

                public void Reset()
                {
                    // per http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx
                    throw new NotSupportedException();
                }
            }

            EnumeratorWrapper<T> wrapper;
            int chunkSize;
            int start;

            public ChunkedEnumerable(EnumeratorWrapper<T> wrapper, int chunkSize, int start)
            {
                this.wrapper = wrapper;
                this.chunkSize = chunkSize;
                this.start = start;
            }

            public IEnumerator<T> GetEnumerator()
            {
                return new ChildEnumerator(this);
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

        }

        class EnumeratorWrapper<T>
        {
            public EnumeratorWrapper (IEnumerable<T> source)
            {
                SourceEumerable = source;
            }
            IEnumerable<T> SourceEumerable {get; set;}

            Enumeration currentEnumeration;

            class Enumeration
            {
                public IEnumerator<T> Source { get; set; }
                public int Position { get; set; }
                public bool AtEnd { get; set; }
            }

            public bool Get(int pos, out T item) 
            {

                if (currentEnumeration != null && currentEnumeration.Position > pos)
                {
                    currentEnumeration.Source.Dispose();
                    currentEnumeration = null;
                }

                if (currentEnumeration == null)
                {
                    currentEnumeration = new Enumeration { Position = -1, Source = SourceEumerable.GetEnumerator(), AtEnd = false };
                }

                item = default(T);
                if (currentEnumeration.AtEnd)
                {
                    return false;
                }

                while(currentEnumeration.Position < pos) 
                {
                    currentEnumeration.AtEnd = !currentEnumeration.Source.MoveNext();
                    currentEnumeration.Position++;

                    if (currentEnumeration.AtEnd) 
                    {
                        return false;
                    }

                }

                item = currentEnumeration.Source.Current;

                return true;
            }

            int refs = 0;

            // needed for dispose semantics 
            public void AddRef()
            {
                refs++;
            }

            public void RemoveRef()
            {
                refs--;
                if (refs == 0 && currentEnumeration != null)
                {
                    var copy = currentEnumeration;
                    currentEnumeration = null;
                    copy.Source.Dispose();
                }
            }
        }

        public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunksize)
        {
            if (chunksize < 1) throw new InvalidOperationException();

            var wrapper =  new EnumeratorWrapper<T>(source);

            int currentPos = 0;
            T ignore;
            try
            {
                wrapper.AddRef();
                while (wrapper.Get(currentPos, out ignore))
                {
                    yield return new ChunkedEnumerable<T>(wrapper, chunksize, currentPos);
                    currentPos += chunksize;
                }
            }
            finally
            {
                wrapper.RemoveRef();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int i = 10;
            foreach (var group in Enumerable.Range(1, int.MaxValue).Skip(10000000).Chunk(3))
            {
                foreach (var n in group)
                {
                    Console.Write(n);
                    Console.Write(" ");
                }
                Console.WriteLine();
                if (i-- == 0) break;
            }


            var stuffs = Enumerable.Range(1, 10).Chunk(2).ToArray();

            foreach (var idx in new [] {3,2,1})
            {
                Console.Write("idx " + idx + " ");
                foreach (var n in stuffs[idx])
                {
                    Console.Write(n);
                    Console.Write(" ");
                }
                Console.WriteLine();
            }

            /*

10000001 10000002 10000003
10000004 10000005 10000006
10000007 10000008 10000009
10000010 10000011 10000012
10000013 10000014 10000015
10000016 10000017 10000018
10000019 10000020 10000021
10000022 10000023 10000024
10000025 10000026 10000027
10000028 10000029 10000030
10000031 10000032 10000033
idx 3 7 8
idx 2 5 6
idx 1 3 4
             */

            Console.ReadKey();


        }

    }
}

चॉइस के आउट-ऑफ-ऑर्डर पुनरावृत्ति के लिए आपके द्वारा प्रस्तुत किए जा सकने वाले अनुकूलन का एक दौर भी है, जो यहां दायरे से बाहर है।

आपको किस विधि का चयन करना चाहिए? यह पूरी तरह से उस समस्या पर निर्भर करता है जिसे आप हल करने की कोशिश कर रहे हैं। यदि आप पहले दोष से चिंतित नहीं हैं तो सरल उत्तर अविश्वसनीय रूप से आकर्षक है।

अधिकांश तरीकों के साथ ध्यान दें , यह मल्टी थ्रेडिंग के लिए सुरक्षित नहीं है, सामान अजीब हो सकता है यदि आप इसे थ्रेड को सुरक्षित बनाना चाहते हैं तो आपको संशोधन करने की आवश्यकता होगी EnumeratorWrapper


क्या बग Enumerable.Range (0, 100) .हंक (3) .Rerseerse ()? ToArray () गलत हो रहा है, या Enumerable.Range (0, 100) .ToArray ()) Chunk (3) .Rerseerse ()। .ToArray () एक अपवाद फेंक?
कैमरन मैकफारलैंड

@SamSaffron मैंने अपना उत्तर अपडेट कर लिया है और जो मुझे लगता है कि यह प्रमुख उपयोग का मामला है (और कैविट्स को स्वीकार करता हूं) के लिए कोड को बहुत सरल बना दिया है।
कैस्पर ओने

IQueryable <> को चून करने के बारे में क्या? मेरा अनुमान है कि यदि हम प्रदाता को अधिकतम परिचालन सौंपना चाहते हैं तो एक टेक / स्किप एप्रोच इष्टतम होगा
ग्वेलियुम

@ Guillaume86 मैं सहमत हूं, यदि आपके पास एक आईएलआईएस या आईक्यूएबल है, तो आप सभी प्रकार के शॉर्टकट ले सकते हैं जो इसे बहुत तेज बना देगा (लिनक यह आंतरिक रूप से अन्य तरीकों के लिए करता है)
सैम केसर

1
यह दक्षता के लिए अब तक का सबसे अच्छा जवाब है। मैं एक IEnumerable के साथ SqlBulkCopy का उपयोग कर एक समस्या है, जो प्रत्येक स्तंभ पर अतिरिक्त प्रक्रियाओं को चलाता है, इसलिए इसे केवल एक पास के साथ कुशलता से चलना चाहिए। यह मुझे IEnumerable को प्रबंधनीय आकार के विखंडू में तोड़ने की अनुमति देगा। (सोच रहे लोगों के लिए, मैंने SqlBulkCopy के स्ट्रीमिंग मोड को सक्षम किया, जो लगता है कि टूट गया है)।
ब्रेन २२

64

आप सकता है प्रश्नों का उपयोग करने वाले के एक नंबर का उपयोग Takeऔर Skip, लेकिन यह मूल सूची में भी कई पुनरावृत्तियों जोड़ना होगा, मेरा मानना है।

बल्कि, मुझे लगता है कि आपको अपना स्वयं का एक इट्रेटर बनाना चाहिए, जैसे:

public static IEnumerable<IEnumerable<T>> GetEnumerableOfEnumerables<T>(
  IEnumerable<T> enumerable, int groupSize)
{
   // The list to return.
   List<T> list = new List<T>(groupSize);

   // Cycle through all of the items.
   foreach (T item in enumerable)
   {
     // Add the item.
     list.Add(item);

     // If the list has the number of elements, return that.
     if (list.Count == groupSize)
     {
       // Return the list.
       yield return list;

       // Set the list to a new list.
       list = new List<T>(groupSize);
     }
   }

   // Return the remainder if there is any,
   if (list.Count != 0)
   {
     // Return the list.
     yield return list;
   }
}

तब आप इसे कॉल कर सकते हैं और यह LINQ सक्षम है ताकि आप परिणामी दृश्यों पर अन्य ऑपरेशन कर सकें।


सैम के जवाब के प्रकाश में , मुझे लगा कि इसके बिना ऐसा करने का एक आसान तरीका है:

  • सूची के माध्यम से पुन: परिवर्तन (जो मैंने मूल रूप से नहीं किया था)
  • चंक जारी करने से पहले समूहों में वस्तुओं को भौतिक बनाना (बड़ी मात्रा में वस्तुओं के लिए, मेमोरी के मुद्दे होंगे)
  • सैम द्वारा पोस्ट किए गए सभी कोड

जैसा कि कहा गया है, यहाँ एक और पास है, जो मैं के लिए एक विस्तार विधि में संहिताबद्ध किया है IEnumerable<T>कहा जाता है Chunk:

public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, 
    int chunkSize)
{
    // Validate parameters.
    if (source == null) throw new ArgumentNullException("source");
    if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize",
        "The chunkSize parameter must be a positive value.");

    // Call the internal implementation.
    return source.ChunkInternal(chunkSize);
}

वहाँ कुछ भी आश्चर्य नहीं, बस बुनियादी त्रुटि जाँच।

इस पर आगे बढ़ना ChunkInternal:

private static IEnumerable<IEnumerable<T>> ChunkInternal<T>(
    this IEnumerable<T> source, int chunkSize)
{
    // Validate parameters.
    Debug.Assert(source != null);
    Debug.Assert(chunkSize > 0);

    // Get the enumerator.  Dispose of when done.
    using (IEnumerator<T> enumerator = source.GetEnumerator())
    do
    {
        // Move to the next element.  If there's nothing left
        // then get out.
        if (!enumerator.MoveNext()) yield break;

        // Return the chunked sequence.
        yield return ChunkSequence(enumerator, chunkSize);
    } while (true);
}

मूल रूप से, यह IEnumerator<T>प्रत्येक आइटम के माध्यम से मैन्युअल रूप से प्रसारित हो जाता है । यह देखने के लिए जांचता है कि क्या वर्तमान में कोई आइटम एनुमरेटेड है। प्रत्येक चंक के माध्यम से एनुमरेट किया जाता है, अगर कोई आइटम नहीं बचा है, तो यह टूट जाता है।

एक बार जब यह पता चलता है कि अनुक्रम में आइटम हैं, तो यह आंतरिक IEnumerable<T>कार्यान्वयन के लिए जिम्मेदारी को दर्शाता है ChunkSequence:

private static IEnumerable<T> ChunkSequence<T>(IEnumerator<T> enumerator, 
    int chunkSize)
{
    // Validate parameters.
    Debug.Assert(enumerator != null);
    Debug.Assert(chunkSize > 0);

    // The count.
    int count = 0;

    // There is at least one item.  Yield and then continue.
    do
    {
        // Yield the item.
        yield return enumerator.Current;
    } while (++count < chunkSize && enumerator.MoveNext());
}

चूंकि MoveNextपहले से ही इसे IEnumerator<T>पास करने के लिए बुलाया गया था ChunkSequence, इसलिए यह आइटम द्वारा लौटाए गए पैदावार को Currentबढ़ाता है और फिर गणना में वृद्धि करता है, जिससे यह सुनिश्चित होता है कि chunkSizeआइटम से अधिक कभी नहीं लौटाएं और प्रत्येक पुनरावृत्ति के बाद अनुक्रम में अगले आइटम पर जा रहे हैं (लेकिन शॉर्ट-सर्कुलेट किया गया है यदि संख्या प्राप्त उपज चंक आकार से अधिक है)।

यदि कोई आइटम नहीं बचा है, तो InternalChunkविधि बाहरी लूप में एक और पास करेगी, लेकिन जब MoveNextदूसरी बार कहा जाता है, तो यह अभी भी दस्तावेज़ (जोर मेरा) के अनुसार , झूठी वापस आ जाएगी :

यदि MoveNext संग्रह के अंत से गुजरता है, तो संग्रह में अंतिम तत्व के बाद Enumerator तैनात किया जाता है और MoveNext गलत हो जाता है। जब एन्यूमरेटर इस स्थिति में होता है, तो बाद में MoveNext को कॉल भी झूठी हो जाती है जब तक कि रीसेट को कॉल नहीं किया जाता है।

इस बिंदु पर, लूप टूट जाएगा, और अनुक्रमों का क्रम समाप्त हो जाएगा।

यह एक सरल परीक्षण है:

static void Main()
{
    string s = "agewpsqfxyimc";

    int count = 0;

    // Group by three.
    foreach (IEnumerable<char> g in s.Chunk(3))
    {
        // Print out the group.
        Console.Write("Group: {0} - ", ++count);

        // Print the items.
        foreach (char c in g)
        {
            // Print the item.
            Console.Write(c + ", ");
        }

        // Finish the line.
        Console.WriteLine();
    }
}

आउटपुट:

Group: 1 - a, g, e,
Group: 2 - w, p, s,
Group: 3 - q, f, x,
Group: 4 - y, i, m,
Group: 5 - c,

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

इसके अतिरिक्त, यह अजीब चीजें करेगा यदि आप आदेश के साथ खेलते हैं, जैसे कि सैम ने एक बिंदु पर किया था


मुझे लगता है कि यह सबसे अच्छा समाधान है ... एकमात्र समस्या यह है कि सूची में लंबाई नहीं है ... इसकी गणना है। लेकिन इसे बदलना आसान है। हम सूची का निर्माण न करके इसे बेहतर बना सकते हैं, लेकिन ienumerables को वापस कर सकते हैं जिसमें ऑफसेट / लंबाई संयोजन के साथ मुख्य सूची के संदर्भ होते हैं। इसलिए, यदि समूह बड़ा है, तो हम मेमोरी बर्बाद नहीं करते हैं। टिप्पणी करें यदि आप चाहते हैं कि मैं इसे लिखूं।
आमिर

@Amir मैं देखना चाहता हूं कि लिखा
samandmoore

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

@SamSaffron हाँ, यदि आपके पास बड़ी संख्या में आइटम List<T>हैं, तो आप स्पष्ट रूप से बफ़रिंग के कारण स्मृति समस्याएँ होने वाले हैं। रेट्रोस्पेक्ट में, मुझे उस उत्तर में ध्यान देना चाहिए था, लेकिन यह उस समय लग रहा था जब बहुत अधिक पुनरावृत्तियों पर ध्यान केंद्रित किया गया था। उस ने कहा, आपका समाधान वास्तव में बालों का है। मैंने इसका परीक्षण नहीं किया है, लेकिन अब मुझे आश्चर्य होता है कि क्या कम बालों वाला घोल है।
कैस्परऑन

@casperOne हाँ ... Google ने मुझे यह पृष्ठ दिया, जब मैं enumerables को विभाजित करने का एक तरीका खोज रहा था, अपने विशिष्ट उपयोग के मामले में मैं उन रिकॉर्डों की एक बड़ी सूची को विभाजित कर रहा हूं जो db से लौटाए गए हैं, अगर मैं उन्हें इन में बदल दें सूची इसे उड़ा देगी (वास्तव में डैपर में एक बफर है: केवल इस उपयोग के मामले के लिए गलत विकल्प)
सैम केसर

48

ठीक है, यहाँ इस पर ले रहा हूँ:

  • पूरी तरह से आलसी: अनंत enumerables पर काम करता है
  • कोई मध्यवर्ती नकल / बफरिंग नहीं
  • ओ (एन) निष्पादन का समय
  • यह भी काम करता है जब आंतरिक अनुक्रम केवल आंशिक रूप से खपत होते हैं

public static IEnumerable<IEnumerable<T>> Chunks<T>(this IEnumerable<T> enumerable,
                                                    int chunkSize)
{
    if (chunkSize < 1) throw new ArgumentException("chunkSize must be positive");

    using (var e = enumerable.GetEnumerator())
    while (e.MoveNext())
    {
        var remaining = chunkSize;    // elements remaining in the current chunk
        var innerMoveNext = new Func<bool>(() => --remaining > 0 && e.MoveNext());

        yield return e.GetChunk(innerMoveNext);
        while (innerMoveNext()) {/* discard elements skipped by inner iterator */}
    }
}

private static IEnumerable<T> GetChunk<T>(this IEnumerator<T> e,
                                          Func<bool> innerMoveNext)
{
    do yield return e.Current;
    while (innerMoveNext());
}

उदाहरण उपयोग

var src = new [] {1, 2, 3, 4, 5, 6}; 

var c3 = src.Chunks(3);      // {{1, 2, 3}, {4, 5, 6}}; 
var c4 = src.Chunks(4);      // {{1, 2, 3, 4}, {5, 6}}; 

var sum   = c3.Select(c => c.Sum());    // {6, 15}
var count = c3.Count();                 // 2
var take2 = c3.Select(c => c.Take(2));  // {{1, 2}, {4, 5}}

स्पष्टीकरण

कोड दो yieldआधारित पुनरावृत्तियों को नेस्ट करके काम करता है ।

आउटर इटरेटर को इस बात का ध्यान रखना चाहिए कि कितने तत्व प्रभावी रूप से इनर (चंक) इटरेटर द्वारा उपभोग किए गए हैं। यह remainingसाथ में बंद करके किया जाता है innerMoveNext()। एक बाहरी भाग के द्वारा अगले चंक की पैदावार करने से पहले एक चंक के असंगत तत्वों को छोड़ दिया जाता है। यह आवश्यक है क्योंकि अन्यथा आप असंगत परिणाम प्राप्त करते हैं, जब आंतरिक प्रगणक पूरी तरह से भस्म नहीं होते हैं (जैसे c3.Count()कि 6 वापस आएंगे)।

नोट: उत्तर को @aolszowka द्वारा इंगित कमियों को संबोधित करने के लिए अद्यतन किया गया है।


2
बहुत अच्छा। मेरा "सही" समाधान उस से अधिक जटिल था। यह # 1 उत्तर IMHO है।
केसीबी

जब टोआरे () कहा जाता है, तो यह अप्रत्याशित (एपीआई दृष्टिकोण से) व्यवहार से ग्रस्त है, यह थ्रेड-सुरक्षित भी नहीं है।
एल्सज़ोवका

@aolszowka: क्या आप कृपया विस्तृत कर सकते हैं?
3dGrabber

@ 3dGrabber शायद यह था कि मैंने आपके कोड को फिर से कैसे प्राप्त किया (खेद है कि यह थोड़ा बहुत लंबे समय तक यहां रहेगा, मूल रूप से एक विस्तार विधि के बजाय मैं स्रोतइंटरमीटर में पारित हुआ)। मैंने जिस परीक्षण केस का उपयोग किया वह इस आशय के लिए कुछ था: int [] arrayToSort = new int [] {9, 7, 2, 6, 3, 4, 8, 5, 1, 10, 11, 12, 13}; var source = chunkify <int> (arrayToSort, 3) .ToArray (); स्रोत में परिणाम यह दर्शाता है कि 13 विखंडू (तत्वों की संख्या) थे। यह मेरे लिए समझ में आता है जब तक कि आप भीतर के एनुमरेटर की व्याख्या नहीं करते थे एन्यूमरेटर बढ़े हुए नहीं थे।
एल्सज़ोवका

1
@aolszowka: बहुत ही मान्य बिंदु। मैंने एक चेतावनी और एक उपयोग अनुभाग जोड़ा है। कोड मानता है कि आप भीतर के असंख्य पर पुनरावृति करते हैं। अपने समाधान के साथ आप आलस्य को रोकते हैं। मुझे लगता है कि IEnumerator कैशिंग के साथ एक कस्टम के साथ दोनों दुनिया का सर्वश्रेष्ठ प्राप्त करना संभव होना चाहिए। अगर मुझे इसका हल मिल जाए तो मैं इसे यहाँ पोस्ट कर
दूंगा

18

पूरी तरह से आलसी, कोई गिनती या नकल नहीं:

public static class EnumerableExtensions
{

  public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int len)
  {
     if (len == 0)
        throw new ArgumentNullException();

     var enumer = source.GetEnumerator();
     while (enumer.MoveNext())
     {
        yield return Take(enumer.Current, enumer, len);
     }
  }

  private static IEnumerable<T> Take<T>(T head, IEnumerator<T> tail, int len)
  {
     while (true)
     {
        yield return head;
        if (--len == 0)
           break;
        if (tail.MoveNext())
           head = tail.Current;
        else
           break;
     }
  }
}

यह समाधान इतना सुरुचिपूर्ण है कि मुझे खेद है कि मैं इस उत्तर को एक से अधिक बार नहीं बना सकता।
मार्क

3
मुझे नहीं लगता कि यह कभी असफल होगा, बिल्कुल। लेकिन यह निश्चित रूप से कुछ अजीब व्यवहार हो सकता है। यदि आपके पास 100 आइटम थे, और आप 10 के बैचों में विभाजित हो गए, और आपने उन बैचों के किसी भी आइटम की गणना किए बिना सभी बैचों की गणना की, तो आप 100 बैचों के साथ समाप्त हो
जाएंगे

1
जैसा कि @ कैसीबी ने उल्लेख किया है, यह उसी असफल 3DGrabber से ग्रस्त है जिसे यहां stackoverflow.com/a/20953521/1037948 पर संबोधित किया गया है , लेकिन आदमी यह तेज है!
ड्रूज़

1
यह एक सुंदर उपाय है। जैसा वादा करता है वैसा ही करता है।
रॉड हर्टेलेल

अब तक सबसे सुरुचिपूर्ण और बिंदु समाधान तक। केवल एक चीज है, आपको नकारात्मक संख्याओं के लिए एक चेक जोड़ना चाहिए, और ArgumentNullException को एक ArgumentException द्वारा प्रतिस्थापित करना चाहिए
Romain Vergnory

13

मुझे लगता है कि निम्नलिखित सुझाव सबसे तेज होगा। मैं Array.Copy का उपयोग करने की क्षमता के लिए स्रोत की आलसीता का त्याग कर रहा हूं और अपने प्रत्येक उप्लब्धियों की लंबाई के समय से पहले जान रहा हूं।

public static IEnumerable<T[]> Chunk<T>(this IEnumerable<T> items, int size)
{
    T[] array = items as T[] ?? items.ToArray();
    for (int i = 0; i < array.Length; i+=size)
    {
        T[] chunk = new T[Math.Min(size, array.Length - i)];
        Array.Copy(array, i, chunk, 0, chunk.Length);
        yield return chunk;
    }
}

न केवल सबसे तेज़, यह सही ढंग से परिणाम पर आगे बढ़े हुए संचालन को भी संभालता है, अर्थात् आइटम। हंक (5)। रिवर्स ()। SelectMany (x => x)
भी

9

हम सही आलसी मूल्यांकन करने के लिए @ JaredPar के समाधान में सुधार कर सकते हैं। हम एक ऐसी GroupAdjacentByविधि का उपयोग करते हैं जो एक ही कुंजी के साथ लगातार तत्वों के समूहों का उत्पादन करती है:

sequence
.Select((x, i) => new { Value = x, Index = i })
.GroupAdjacentBy(x=>x.Index/3)
.Select(g=>g.Select(x=>x.Value))

क्योंकि समूह एक-एक करके उपजते हैं, यह समाधान लंबे या अनंत दृश्यों के साथ कुशलता से काम करता है।


8

मैंने कई साल पहले एक क्लंप एक्सटेंशन विधि लिखी थी। महान काम करता है, और यहाँ सबसे तेज़ कार्यान्वयन है। : पी

/// <summary>
/// Clumps items into same size lots.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source list of items.</param>
/// <param name="size">The maximum size of the clumps to make.</param>
/// <returns>A list of list of items, where each list of items is no bigger than the size given.</returns>
public static IEnumerable<IEnumerable<T>> Clump<T>(this IEnumerable<T> source, int size)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (size < 1)
        throw new ArgumentOutOfRangeException("size", "size must be greater than 0");

    return ClumpIterator<T>(source, size);
}

private static IEnumerable<IEnumerable<T>> ClumpIterator<T>(IEnumerable<T> source, int size)
{
    Debug.Assert(source != null, "source is null.");

    T[] items = new T[size];
    int count = 0;
    foreach (var item in source)
    {
        items[count] = item;
        count++;

        if (count == size)
        {
            yield return items;
            items = new T[size];
            count = 0;
        }
    }
    if (count > 0)
    {
        if (count == size)
            yield return items;
        else
        {
            T[] tempItems = new T[count];
            Array.Copy(items, tempItems, count);
            yield return tempItems;
        }
    }
}

यह काम करना चाहिए, लेकिन यह 100% हिस्सा बफ़र कर रहा है, मैं इससे बचने की कोशिश कर रहा था ... लेकिन यह अविश्वसनीय रूप से बालों वाला होता है।
सैम केसर

@SamSaffron हां। विशेष रूप से यदि आप मिश्रण में प्लिनक जैसी चीजों को फेंकते हैं, जो कि मेरा कार्यान्वयन मूल रूप से था।
कैमरन मैकफारलैंड

मेरे उत्तर का विस्तार करें, मुझे बताएं कि आप क्या सोचते हैं
सैम केसर

@CameronMacFarland - क्या आप बता सकते हैं कि गिनती == आकार की दूसरी जांच क्यों आवश्यक है? धन्यवाद।
डगस

8

System.InteractiveBuffer() इस उद्देश्य के लिए प्रदान करता है । कुछ त्वरित परीक्षण से पता चलता है कि प्रदर्शन सैम के समाधान के समान है।


1
क्या आप बफरिंग शब्दार्थ को जानते हैं? उदाहरण के लिए: यदि आपके पास एक एन्यूमरेटर है जो 300k बड़ा है और इसे 10,000 आकार के विखंडू में विभाजित करने की कोशिश करता है तो क्या आप मेमोरी से बाहर निकलेंगे?
सैम केसर

Buffer()रिटर्न IEnumerable<IList<T>>हाँ, तो आप शायद एक समस्या होगा - यह आपके जैसे धारा नहीं है।
dahlbyk

7

यहाँ एक नियमित बंटवारे की सूची है जो मैंने कुछ महीने पहले लिखी थी:

public static List<List<T>> Chunk<T>(
    List<T> theList,
    int chunkSize
)
{
    List<List<T>> result = theList
        .Select((x, i) => new {
            data = x,
            indexgroup = i / chunkSize
        })
        .GroupBy(x => x.indexgroup, x => x.data)
        .Select(g => new List<T>(g))
        .ToList();

    return result;
}

6

मुझे लगता है यह थोड़ा स्निपेट काम काफी अच्छी तरह से करता है।

public static IEnumerable<List<T>> Chunked<T>(this List<T> source, int chunkSize)
{
    var offset = 0;

    while (offset < source.Count)
    {
        yield return source.GetRange(offset, Math.Min(source.Count - offset, chunkSize));
        offset += chunkSize;
    }
}

5

और इसका क्या?

var input = new List<string> { "a", "g", "e", "w", "p", "s", "q", "f", "x", "y", "i", "m", "c" };
var k = 3

var res = Enumerable.Range(0, (input.Count - 1) / k + 1)
                    .Select(i => input.GetRange(i * k, Math.Min(k, input.Count - i * k)))
                    .ToList();

जहाँ तक मुझे पता है, GetRange () ली गई वस्तुओं की संख्या के मामले में रैखिक है। इसलिए यह अच्छा प्रदर्शन करना चाहिए।


5

यह एक पुराना सवाल है, लेकिन यह वही है जो मैंने समाप्त किया है; यह केवल एक बार गणना करने योग्य है, लेकिन प्रत्येक विभाजन के लिए सूची बनाता है। यह अप्रत्याशित व्यवहार से ग्रस्त नहीं है जब ToArray()कुछ कार्यान्वयन के रूप में कहा जाता है:

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

        if (chunkSize < 1)
        {
            throw new ArgumentException("Invalid chunkSize: " + chunkSize);
        }

        using (IEnumerator<T> sourceEnumerator = source.GetEnumerator())
        {
            IList<T> currentChunk = new List<T>();
            while (sourceEnumerator.MoveNext())
            {
                currentChunk.Add(sourceEnumerator.Current);
                if (currentChunk.Count == chunkSize)
                {
                    yield return currentChunk;
                    currentChunk = new List<T>();
                }
            }

            if (currentChunk.Any())
            {
                yield return currentChunk;
            }
        }
    }

इसे विस्तार विधि में बदलना अच्छा होगा:public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int chunkSize)
krizzzn

आपके उत्तर के लिए +1। हालाँकि मैं दो चीजों की सलाह देता हूं। 1. जबकि ब्लॉक और ब्लॉक का उपयोग करते हुए फॉर्चून का उपयोग करें। 2. सूची के निर्माण में chunkSize पास करें ताकि सूची को इसका अधिकतम अपेक्षित आकार पता हो।
उस्मान ज़फर

4

हमने पाया कि डेविड बी के समाधान ने सबसे अच्छा काम किया। लेकिन हमने इसे और अधिक सामान्य समाधान के लिए अनुकूलित किया:

list.GroupBy(item => item.SomeProperty) 
   .Select(group => new List<T>(group)) 
   .ToArray();

3
यह अच्छा है, लेकिन मूल पूछने वाले के लिए काफी अलग है।
एमी बी

4

यह निम्नलिखित समाधान सबसे कॉम्पैक्ट है जो मैं ओ (एन) के साथ आ सकता हूं।

public static IEnumerable<T[]> Chunk<T>(IEnumerable<T> source, int chunksize)
{
    var list = source as IList<T> ?? source.ToList();
    for (int start = 0; start < list.Count; start += chunksize)
    {
        T[] chunk = new T[Math.Min(chunksize, list.Count - start)];
        for (int i = 0; i < chunk.Length; i++)
            chunk[i] = list[start + i];

        yield return chunk;
    }
}

4

पुराना कोड, लेकिन यह वही है जो मैं उपयोग कर रहा हूं:

    public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        var toReturn = new List<T>(max);
        foreach (var item in source)
        {
            toReturn.Add(item);
            if (toReturn.Count == max)
            {
                yield return toReturn;
                toReturn = new List<T>(max);
            }
        }
        if (toReturn.Any())
        {
            yield return toReturn;
        }
    }

पोस्ट करने के बाद, मुझे एहसास हुआ कि यह बिलकुल उसी कोड कास्परऑने का उपयोग करने के बदलाव के साथ 6 साल पहले पोस्ट किया गया है। इसके बजाय। () के बजाय। () के रूप में मुझे पूरी गिनती की आवश्यकता नहीं है, बस यह जानने की आवश्यकता है कि क्या कोई मौजूद है ।
रॉबर्ट मैककी

3

यदि सूची प्रकार system.collections.generic की है, तो आप अपने सरणी के तत्वों को अन्य उप सरणियों में कॉपी करने के लिए उपलब्ध "CopyTo" विधि का उपयोग कर सकते हैं। आप प्रतिलिपि करने के लिए प्रारंभ तत्व और तत्वों की संख्या निर्दिष्ट करते हैं।

आप अपनी मूल सूची के 3 क्लोन भी बना सकते हैं और सूची को आपके इच्छित आकार को सिकोड़ने के लिए प्रत्येक सूची पर "RemoveRange" का उपयोग कर सकते हैं।

या बस आपके लिए यह करने के लिए एक सहायक विधि बनाएं।


2

यह एक पुराना समाधान है लेकिन मेरा एक अलग दृष्टिकोण था। मैं Skipवांछित ऑफ़सेट पर जाने के लिए और Takeवांछित तत्वों को निकालने के लिए उपयोग करता हूं :

public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, 
                                                   int chunkSize)
{
    if (chunkSize <= 0)
        throw new ArgumentOutOfRangeException($"{nameof(chunkSize)} should be > 0");

    var nbChunks = (int)Math.Ceiling((double)source.Count()/chunkSize);

    return Enumerable.Range(0, nbChunks)
                     .Select(chunkNb => source.Skip(chunkNb*chunkSize)
                     .Take(chunkSize));
}

1
मेरे द्वारा उपयोग किए गए दृष्टिकोण के समान, लेकिन मैं उस स्रोत को IEnumerable नहीं होने की सलाह देता हूं। उदाहरण के लिए, यदि स्रोत LINQ क्वेरी का परिणाम है, तो छोड़ें / लें क्वेरी के nbChunk enumerations को ट्रिगर करेगा। महंगा हो सकता है। स्रोत के प्रकार के रूप में IList या ICollection का उपयोग करना बेहतर होगा। कि समस्या से पूरी तरह से बचा जाता है।
आरबी डेविडसन

2

पैक किए गए / बनाए गए समाधान के इच्छुक किसी भी व्यक्ति के लिए, MoreLINQ लाइब्रेरी Batchएक्सटेंशन विधि प्रदान करती है जो आपके अनुरोधित व्यवहार से मेल खाती है:

IEnumerable<char> source = "Example string";
IEnumerable<IEnumerable<char>> chunksOfThreeChars = source.Batch(3);

Batchकार्यान्वयन के समान है कैमरून MacFarland का जवाब , लौटने से पहले हिस्सा / बैच बदलने के लिए एक अधिभार के योग के साथ, और प्रदर्शन काफी अच्छी तरह से।


यह स्वीकृत उत्तर होना चाहिए। पहिया पुनर्रचना की बजाय, morelinq इस्तेमाल किया जाना चाहिए
Otabek Kholikov

1

मॉड्यूलर विभाजन का उपयोग करना:

public IEnumerable<IEnumerable<string>> Split(IEnumerable<string> input, int chunkSize)
{
    var chunks = (int)Math.Ceiling((double)input.Count() / (double)chunkSize);
    return Enumerable.Range(0, chunks).Select(id => input.Where(s => s.GetHashCode() % chunks == id));
}

1

बस मेरे दो सेंट में डाल दिया। यदि आप सूची को "बकेट" करना चाहते हैं (बाएं से दाएं देखें), तो आप निम्न कार्य कर सकते हैं:

 public static List<List<T>> Buckets<T>(this List<T> source, int numberOfBuckets)
    {
        List<List<T>> result = new List<List<T>>();
        for (int i = 0; i < numberOfBuckets; i++)
        {
            result.Add(new List<T>());
        }

        int count = 0;
        while (count < source.Count())
        {
            var mod = count % numberOfBuckets;
            result[mod].Add(source[count]);
            count++;
        }
        return result;
    }

1

दूसरा तरीका Rx बफर ऑपरेटर का उपयोग कर रहा है

//using System.Linq;
//using System.Reactive.Linq;
//using System.Reactive.Threading.Tasks;

var observableBatches = anAnumerable.ToObservable().Buffer(size);

var batches = aList.ToObservable().Buffer(size).ToList().ToTask().GetAwaiter().GetResult();

IMHO सबसे अधिक छिद्रपूर्ण उत्तर।
स्टेनिस्लाव बेरकोव

1
public static List<List<T>> GetSplitItemsList<T>(List<T> originalItemsList, short number)
    {
        var listGroup = new List<List<T>>();
        int j = number;
        for (int i = 0; i < originalItemsList.Count; i += number)
        {
            var cList = originalItemsList.Take(j).Skip(i).ToList();
            j += number;
            listGroup.Add(cList);
        }
        return listGroup;
    }

0

मैंने प्राथमिक उत्तर लिया और यह निर्धारित करने के लिए एक आईओसी कंटेनर बना दिया कि कहां विभाजन करना है। ( जो वास्तव में केवल 3 वस्तुओं पर विभाजित करने के लिए देख रहा है, उत्तर की खोज करते हुए इस पोस्ट को पढ़ने में? )

यह विधि आवश्यकतानुसार किसी भी प्रकार के आइटम पर विभाजित करने की अनुमति देती है।

public static List<List<T>> SplitOn<T>(List<T> main, Func<T, bool> splitOn)
{
    int groupIndex = 0;

    return main.Select( item => new 
                             { 
                               Group = (splitOn.Invoke(item) ? ++groupIndex : groupIndex), 
                               Value = item 
                             })
                .GroupBy( it2 => it2.Group)
                .Select(x => x.Select(v => v.Value).ToList())
                .ToList();
}

तो ओपी के लिए कोड होगा

var it = new List<string>()
                       { "a", "g", "e", "w", "p", "s", "q", "f", "x", "y", "i", "m", "c" };

int index = 0; 
var result = SplitOn(it, (itm) => (index++ % 3) == 0 );

0

तो सैम सैफ्रन के दृष्टिकोण के रूप में प्रदर्शन ।

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size), "Size must be greater than zero.");

    return BatchImpl(source, size).TakeWhile(x => x.Any());
}

static IEnumerable<IEnumerable<T>> BatchImpl<T>(this IEnumerable<T> source, int size)
{
    var values = new List<T>();
    var group = 1;
    var disposed = false;
    var e = source.GetEnumerator();

    try
    {
        while (!disposed)
        {
            yield return GetBatch(e, values, group, size, () => { e.Dispose(); disposed = true; });
            group++;
        }
    }
    finally
    {
        if (!disposed)
            e.Dispose();
    }
}

static IEnumerable<T> GetBatch<T>(IEnumerator<T> e, List<T> values, int group, int size, Action dispose)
{
    var min = (group - 1) * size + 1;
    var max = group * size;
    var hasValue = false;

    while (values.Count < min && e.MoveNext())
    {
        values.Add(e.Current);
    }

    for (var i = min; i <= max; i++)
    {
        if (i <= values.Count)
        {
            hasValue = true;
        }
        else if (hasValue = e.MoveNext())
        {
            values.Add(e.Current);
        }
        else
        {
            dispose();
        }

        if (hasValue)
            yield return values[i - 1];
        else
            yield break;
    }
}

}


0

अनंत जनरेटर के साथ काम कर सकते हैं:

a.Zip(a.Skip(1), (x, y) => Enumerable.Repeat(x, 1).Concat(Enumerable.Repeat(y, 1)))
 .Zip(a.Skip(2), (xy, z) => xy.Concat(Enumerable.Repeat(z, 1)))
 .Where((x, i) => i % 3 == 0)

डेमो कोड: https://ideone.com/GKmL7M

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

public class Test
{
  private static void DoIt(IEnumerable<int> a)
  {
    Console.WriteLine(String.Join(" ", a));

    foreach (var x in a.Zip(a.Skip(1), (x, y) => Enumerable.Repeat(x, 1).Concat(Enumerable.Repeat(y, 1))).Zip(a.Skip(2), (xy, z) => xy.Concat(Enumerable.Repeat(z, 1))).Where((x, i) => i % 3 == 0))
      Console.WriteLine(String.Join(" ", x));

    Console.WriteLine();
  }

  public static void Main()
  {
    DoIt(new int[] {1});
    DoIt(new int[] {1, 2});
    DoIt(new int[] {1, 2, 3});
    DoIt(new int[] {1, 2, 3, 4});
    DoIt(new int[] {1, 2, 3, 4, 5});
    DoIt(new int[] {1, 2, 3, 4, 5, 6});
  }
}
1

1 2

1 2 3
1 2 3

1 2 3 4
1 2 3

1 2 3 4 5
1 2 3

1 2 3 4 5 6
1 2 3
4 5 6

लेकिन वास्तव में मैं linq के बिना संबंधित विधि लिखना पसंद करूंगा।


0

इसकी जांच करें! मेरे पास अनुक्रम काउंटर और दिनांक वाले तत्वों की एक सूची है। हर बार अनुक्रम के पुनरारंभ होने के बाद, मैं एक नई सूची बनाना चाहता हूं।

पूर्व। संदेशों की सूची।

 List<dynamic> messages = new List<dynamic>
        {
            new { FcntUp = 101, CommTimestamp = "2019-01-01 00:00:01" },
            new { FcntUp = 102, CommTimestamp = "2019-01-01 00:00:02" },
            new { FcntUp = 103, CommTimestamp = "2019-01-01 00:00:03" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:04" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:05" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:06" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:07" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:08" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:09" }
        };

काउंटर रिस्टार्ट होते ही मैं सूची को अलग-अलग सूचियों में विभाजित करना चाहता हूं। यहाँ कोड है:

var arraylist = new List<List<dynamic>>();

        List<dynamic> messages = new List<dynamic>
        {
            new { FcntUp = 101, CommTimestamp = "2019-01-01 00:00:01" },
            new { FcntUp = 102, CommTimestamp = "2019-01-01 00:00:02" },
            new { FcntUp = 103, CommTimestamp = "2019-01-01 00:00:03" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:04" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:05" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:06" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:07" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:08" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:09" }
        };

        //group by FcntUp and CommTimestamp
        var query = messages.GroupBy(x => new { x.FcntUp, x.CommTimestamp });

        //declare the current item
        dynamic currentItem = null;

        //declare the list of ranges
        List<dynamic> range = null;

        //loop through the sorted list
        foreach (var item in query)
        {
            //check if start of new range
            if (currentItem == null || item.Key.FcntUp < currentItem.Key.FcntUp)
            {
                //create a new list if the FcntUp starts on a new range
                range = new List<dynamic>();

                //add the list to the parent list
                arraylist.Add(range);
            }

            //add the item to the sublist
            range.Add(item);

            //set the current item
            currentItem = item;
        }

-1

मेरे दो सेंट डालने के लिए ...

स्रोत के लिए सूची प्रकार का उपयोग करके chunked होने के बाद, मुझे एक और बहुत कॉम्पैक्ट समाधान मिला:

public static IEnumerable<IEnumerable<TSource>> Chunk<TSource>(this IEnumerable<TSource> source, int chunkSize)
{
    // copy the source into a list
    var chunkList = source.ToList();

    // return chunks of 'chunkSize' items
    while (chunkList.Count > chunkSize)
    {
        yield return chunkList.GetRange(0, chunkSize);
        chunkList.RemoveRange(0, chunkSize);
    }

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