LINQ के साथ `n` भागों में एक संग्रह विभाजित करें?


122

वहाँ एक संग्रह में विभाजित करने के लिए एक अच्छा तरीका है nLINQ वाले भागों ? जरूरी नहीं कि समान रूप से।

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


1
रिटैज्ड: प्रश्न का asp.net से कोई लेना-देना नहीं है। कृपया अपने प्रश्नों को उचित रूप से टैग करें।

आप वास्तव में उन्हें कैसे विभाजित करना चाहते हैं, यदि नहीं भी (अंत के लिए अनुमति देना, निश्चित रूप से)?
मार्क Gravell

1
इस प्रश्न से कौन जुड़ा है? जॉन यह तुम थे :-) अचानक इन सभी जवाबों :-)
शमौन_वेर


@Simon_Weaver मैंने यह स्पष्ट करने की कोशिश की कि आप स्वीकृत उत्तर के आधार पर क्या पूछ रहे हैं। वास्तव में, एक सूची को 'विभाजित' करने के कई तरीके हैं, जिसमें सूची के प्रत्येक तत्व को उसके तत्वों में विघटित करना और उन तथाकथित 'समानांतर' सूची में डालना शामिल है।
jpaugh

जवाबों:


127

एक शुद्ध लाइनक और सबसे सरल समाधान नीचे दिखाया गया है।

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}

3
आप कर सकते हैं: select (IEnumerable <T>) भाग के बजाय part.AsEnumerable () का चयन करें। यह अधिक सुरुचिपूर्ण लगता है।
अगस्त को रात 11:30

2
उन सभी मापांक कार्यों को करना लंबी सूचियों पर थोड़ा महंगा पड़ सकता है।
जोनाथन एलन

8
इंडेक्स में सेलेक्ट ओवरलोड का उपयोग करना बेहतर होगा।
मार्क Gravell

1
मैंने एक प्रतिक्रिया जोड़ी है जो सिंटैक्स का चयन करते हुए अधिभार और विधि का उपयोग करता है
पुनर्मतदान

1
.AsEnumerable()आवश्यक नहीं है, IGrouping <T> पहले से ही एक IEnumerable <T> है।
एलेक्स

58

संपादित करें: ठीक है, ऐसा लगता है कि मैंने प्रश्न को गलत बताया। मैंने इसे "एन टुकड़ों" के बजाय "लंबाई एन के टुकड़े" के रूप में पढ़ा। रवींद्र! उत्तर हटाने पर विचार ...

(मूल उत्तर)

मुझे विश्वास नहीं है कि विभाजन का एक अंतर्निहित तरीका है, हालांकि मैं वस्तुओं में LINQ को जोड़ने के अपने सेट में एक लिखने का इरादा रखता हूं। मार्क ग्रेवेल का यहां कार्यान्वयन है, हालांकि मैं इसे केवल पढ़ने के लिए देखने के लिए संशोधित करूंगा:

public static IEnumerable<IEnumerable<T>> Partition<T>
    (this IEnumerable<T> source, int size)
{
    T[] array = null;
    int count = 0;
    foreach (T item in source)
    {
        if (array == null)
        {
            array = new T[size];
        }
        array[count] = item;
        count++;
        if (count == size)
        {
            yield return new ReadOnlyCollection<T>(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection<T>(array);
    }
}

डारन - मुझे इसे मारो;
मार्क Gravell

3
आप वास्तव में उन "सरणी [गिनती ++]", एह
मार्क Gravell

18
हटाने के लिए धन्यवाद भले ही यह ओपी के लिए एक जवाब नहीं है, मैं एक ही बात चाहता था - लंबाई n के टुकड़े :)।
गिषु

2
@ डायजन: नहीं, यह नहीं है। के उपयोग पर ध्यान दें yield return। यह एक समय में एक बैच की स्मृति में होना आवश्यक है , लेकिन यह सब है।
जॉन स्कीट

1
@ डायजन: सही है - मैं इसके बारे में अनुमान नहीं लगाना चाहूंगा कि यह कैसे समानांतर LINQ विभाजन के साथ बातचीत करता है, ईमानदार होने के लिए :)
जॉन स्कीट

39
static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
            return list.Select((item, index) => new {index, item})
                       .GroupBy(x => x.index % parts)
                       .Select(x => x.Select(y => y.item));
    }
}

28
मुझे एसक्यूएल-स्टाइल लाइनक का एक तर्कहीन नापसंद है, इसलिए यह मेरा पसंदीदा उत्तर है।

1
@ manu08, मैंने उर कोड की कोशिश की है, मेरे पास एक सूची है var dept = {1,2,3,4,5}। बंटवारे के बाद परिणाम dept1 = {1,3,5}और dept2 = { 2,4 }कहाँ है parts = 2। लेकिन परिणाम की मुझे जरूरत है dept1 = {1,2,3}औरdept2 = {4,5}
कार्तिक आर्थिक

3
मेरे पास modulo के साथ एक ही मुद्दा था, इसलिए मैंने उस कॉलम की लंबाई की गणना की जिसके साथ int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);विभाजन किया था .GroupBy(x => x.index / columnLength)। एक नकारात्मक पहलू यह है कि सूची में गणना की गई है।
गोडेई

24

ठीक है, मैं अपनी टोपी रिंग में फेंक दूंगा। मेरे एल्गोरिथ्म के लाभ:

  1. कोई महंगा गुणन, विभाजन या मापांक संचालक नहीं
  2. सभी ऑपरेशन ओ (1) हैं (नीचे नोट देखें)
  3. IEnumerable के लिए काम करता है <> स्रोत (कोई गणना संपत्ति की आवश्यकता)
  4. सरल

कोड:

public static IEnumerable<IEnumerable<T>>
  Section<T>(this IEnumerable<T> source, int length)
{
  if (length <= 0)
    throw new ArgumentOutOfRangeException("length");

  var section = new List<T>(length);

  foreach (var item in source)
  {
    section.Add(item);

    if (section.Count == length)
    {
      yield return section.AsReadOnly();
      section = new List<T>(length);
    }
  }

  if (section.Count > 0)
    yield return section.AsReadOnly();
}

जैसा कि नीचे टिप्पणी में कहा गया है, यह दृष्टिकोण वास्तव में मूल प्रश्न को संबोधित नहीं करता है जो लगभग समान लंबाई के वर्गों की एक निश्चित संख्या के लिए पूछा है। उस ने कहा, आप अभी भी मूल प्रश्न को हल करने के लिए मेरे दृष्टिकोण का उपयोग इस तरह से कर सकते हैं:

myEnum.Section(myEnum.Count() / number_of_sections + 1)

जब इस तरीके से उपयोग किया जाता है, तो दृष्टिकोण अब ओ (1) नहीं है क्योंकि गणना () ऑपरेशन ओ (एन) है।


शानदार - सबसे अच्छा समाधान यहाँ! कुछ अनुकूलन: * प्रत्येक अनुभाग के लिए एक नया बनाने के बजाय लिंक की गई सूची को साफ़ करें। लिंक की गई सूची का संदर्भ कॉल करने वाले को कभी नहीं लौटाया जाता है, इसलिए यह पूरी तरह से सुरक्षित है। * लिंक सूची तब तक न बनाएं जब तक कि आप पहले आइटम तक न पहुंच जाएं - अगर स्रोत खाली है तो इस तरह का कोई आवंटन नहीं है
शैडोचैसर

3
@ShadowChaser MSDN के अनुसार लिंक्डलिस्ट को साफ़ करना O (N) जटिलता है इसलिए यह O (1) के मेरे लक्ष्य को बर्बाद कर देगा। बेशक, आप तर्क दे सकते हैं कि फोरच हे ... (एन) के साथ शुरू करने के लिए ... :)
माइक

4
आपका जवाब सही है, लेकिन सवाल इसके लिए गलत है। आपका उत्तर प्रत्येक चंक के लिए निश्चित आकार के साथ अज्ञात संख्या में विखंडन देता है। लेकिन ओपी एक स्प्लिट फंक्शनलिटी चाहता है, जहां यह किसी भी आकार के साथ चंक की निश्चित संख्या देता है (उम्मीद है कि समान या समान आकार के करीब)। शायद यहां अधिक अनुकूल है stackoverflow.com/questions/3773403/…
nawfal

1
@ क्या आपने इसे बेंचमार्क किया है? मुझे आशा है कि आप जानते हैं कि ओ (1) का मतलब तेजी से नहीं है, इसका मतलब केवल यह है कि विभाजन के लिए आवश्यक समय स्केल नहीं करता है। मैं बस सोच रहा हूं कि ओ (1) से आंखें मूंदने के लिए आपका औचित्य क्या है जब यह सभी वास्तविक जीवन परिदृश्यों के लिए अन्य ओ (एन) की तुलना में धीमा हो सकता है। मैंने इसे एक पागल 10 ^ 8 शक्ति सूची के लिए भी परीक्षण किया और मेरा अभी भी तेज दिखाई दिया। मुझे आशा है कि आप जानते हैं कि मानक संग्रह प्रकार भी नहीं हैं जो 10 ^ 12 आइटम पकड़ सकते हैं ..
nawfal

1
@nawfal - आपके विस्तृत विश्लेषण के लिए धन्यवाद, यह मुझे मेरे पैर की उंगलियों पर रखने में मदद करता है। सामान्य रूप से लिंक्ड सूचियां कुशल अंत-आवेषण के लिए जानी जाती हैं, यही वजह है कि मैंने इसे यहां चुना है। हालाँकि मैंने अभी बेंचमार्क किया है और वास्तव में लिस्ट <> बहुत तेज़ है। मुझे संदेह है कि यह .NET कार्यान्वयन विवरण का कुछ प्रकार है, शायद एक अलग StackOverflow प्रश्न के योग्य है। मैंने आपके सुझाव के अनुसार सूची <> का उपयोग करने के लिए अपने उत्तर को संशोधित कर दिया है। सूची की क्षमता को देखते हुए कि एंड-इंसर्शन अभी भी O (1) है और मेरे मूल डिज़ाइन लक्ष्य को पूरा करता है। मैंने .NET 4.5 में अंतर्निहित .AsReadOnly () पर भी स्विच किया।
माइक

16

यह स्वीकृत उत्तर के समान है, लेकिन बहुत सरल प्रतिनिधित्व है:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}

उपरोक्त विधि IEnumerable<T>समान आकारों के एन संख्या में या समान आकार के करीब विभाजित होती है।

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

उपरोक्त विधि एक विभाजन करती है IEnumerable<T> वांछित निश्चित आकार के टुकड़ों में जिसमें कुल संख्या महत्वहीन होती है - जो कि प्रश्न के बारे में नहीं है।

के साथ समस्या है Splitविधि के , धीमी होने के अलावा, यह है कि यह इस अर्थ में आउटपुट को स्क्रैम्बल करता है कि ग्रुपिंग प्रत्येक स्थिति के लिए i'th मल्टीपल एन के आधार पर की जाएगी, या दूसरे शब्दों में आपको चांस नहीं मिलेगा। मूल क्रम में।

यहां लगभग हर उत्तर या तो आदेश को संरक्षित नहीं करता है, या विभाजन के बारे में है और विभाजन नहीं है, या स्पष्ट रूप से गलत है। यह प्रयास करें जो तेज़ है, आदेश को संरक्षित करता है लेकिन एक lil 'अधिक क्रिया:

public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, 
                                                   int numberOfChunks)
{
    if (numberOfChunks <= 0 || numberOfChunks > items.Count)
        throw new ArgumentOutOfRangeException("numberOfChunks");

    int sizePerPacket = items.Count / numberOfChunks;
    int extra = items.Count % numberOfChunks;

    for (int i = 0; i < numberOfChunks - extra; i++)
        yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);

    int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
    int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
    for (int i = 0; i < extra; i++)
        yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}

यहाँ एक Partitionऑपरेशन के लिए समकक्ष विधि


6

मैं काफी पहले अक्सर पोस्ट किए गए विभाजन फ़ंक्शन का उपयोग कर रहा हूं। इसके बारे में एकमात्र बुरी बात यह थी कि यह पूरी तरह से स्ट्रीमिंग नहीं थी। यह एक समस्या नहीं है यदि आप अपने अनुक्रम में कुछ तत्वों के साथ काम करते हैं। मुझे एक नए समाधान की आवश्यकता थी जब मैंने अपने अनुक्रम में 100.000+ तत्वों के साथ काम करना शुरू किया।

निम्नलिखित समाधान बहुत अधिक जटिल (और अधिक कोड!) है, लेकिन यह बहुत कुशल है।

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

namespace LuvDaSun.Linq
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int partitionSize)
        {
            /*
            return enumerable
                .Select((item, index) => new { Item = item, Index = index, })
                .GroupBy(item => item.Index / partitionSize)
                .Select(group => group.Select(item => item.Item)                )
                ;
            */

            return new PartitioningEnumerable<T>(enumerable, partitionSize);
        }

    }


    class PartitioningEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        IEnumerable<T> _enumerable;
        int _partitionSize;
        public PartitioningEnumerable(IEnumerable<T> enumerable, int partitionSize)
        {
            _enumerable = enumerable;
            _partitionSize = partitionSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new PartitioningEnumerator<T>(_enumerable.GetEnumerator(), _partitionSize);
        }

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


    class PartitioningEnumerator<T> : IEnumerator<IEnumerable<T>>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitioningEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
            _enumerator.Dispose();
        }

        IEnumerable<T> _current;
        public IEnumerable<T> Current
        {
            get { return _current; }
        }
        object IEnumerator.Current
        {
            get { return _current; }
        }

        public void Reset()
        {
            _current = null;
            _enumerator.Reset();
        }

        public bool MoveNext()
        {
            bool result;

            if (_enumerator.MoveNext())
            {
                _current = new PartitionEnumerable<T>(_enumerator, _partitionSize);
                result = true;
            }
            else
            {
                _current = null;
                result = false;
            }

            return result;
        }

    }



    class PartitionEnumerable<T> : IEnumerable<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitionEnumerable(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new PartitionEnumerator<T>(_enumerator, _partitionSize);
        }

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


    class PartitionEnumerator<T> : IEnumerator<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        int _count;
        public PartitionEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
        }

        public T Current
        {
            get { return _enumerator.Current; }
        }
        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }
        public void Reset()
        {
            if (_count > 0) throw new InvalidOperationException();
        }

        public bool MoveNext()
        {
            bool result;

            if (_count < _partitionSize)
            {
                if (_count > 0)
                {
                    result = _enumerator.MoveNext();
                }
                else
                {
                    result = true;
                }
                _count++;
            }
            else
            {
                result = false;
            }

            return result;
        }

    }
}

का आनंद लें!


यह संस्करण IEnumerator का अनुबंध तोड़ता है। Reset कहते समय InvalidOperationException को फेंकना मान्य नहीं है - मुझे विश्वास है कि LINQ एक्सटेंशन विधियों में से कई इस व्यवहार पर निर्भर करती हैं।
शैडोचैस्टर

1
@ShadowChaser मुझे लगता है कि रीसेट () को NotSupportedException को फेंकना चाहिए और सबकुछ ठीक हो जाएगा। MSDN प्रलेखन से: "रीसेट विधि COM इंटरऑपरेबिलिटी के लिए प्रदान की जाती है। इसे अनिवार्य रूप से लागू करने की आवश्यकता नहीं है, इसके बजाय, कार्यान्वयनकर्ता बस NotSupportedException को फेंक सकता है।"
Toong

@toong वाह, तुम सही हो। मुझे यकीन नहीं है कि मैं इस समय के बाद कैसे चूक गया।
शैडोचेयर

यह छोटी गाड़ी है! मुझे ठीक से याद नहीं है, लेकिन (जहाँ तक मुझे याद है) यह अवांछित कदम उठाता है और इससे बदसूरत साइड इफेक्ट हो सकता है (पूर्व के लिए डेटाटाइडर के साथ।)। सबसे अच्छा समाधान यहाँ है (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/…
SalientBrain

4

दिलचस्प धागा। स्प्लिट / विभाजन का एक स्ट्रीमिंग संस्करण प्राप्त करने के लिए, कोई एनामिटर का उपयोग कर सकता है और एनेमेटर से एक्सटेंशन विधियों का उपयोग करके अनुक्रम प्राप्त कर सकता है। उपज का उपयोग करके कार्यात्मक कोड में अनिवार्य कोड बदलना वास्तव में एक बहुत शक्तिशाली तकनीक है।

पहले एक एन्यूमरेटर एक्सटेंशन जो तत्वों की एक गिनती को एक आलसी अनुक्रम में बदल देता है:

public static IEnumerable<T> TakeFromCurrent<T>(this IEnumerator<T> enumerator, int count)
{
    while (count > 0)
    {
        yield return enumerator.Current;
        if (--count > 0 && !enumerator.MoveNext()) yield break;
    }
}

और फिर एक असाधारण विस्तार जो एक अनुक्रम को विभाजित करता है:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> seq, int partitionSize)
{
    var enumerator = seq.GetEnumerator();

    while (enumerator.MoveNext())
    {
        yield return enumerator.TakeFromCurrent(partitionSize);
    }
}

अंतिम परिणाम एक अत्यधिक कुशल, स्ट्रीमिंग और आलसी कार्यान्वयन है जो बहुत ही सरल कोड पर निर्भर करता है।

का आनंद लें!


मैंने शुरू में एक ही चीज़ को क्रमादेशित किया था, लेकिन जब एक नेस्टेड IEnumerable <T "उदाहरण पर कॉल किया जाता है तो पैटर्न टूट जाता है।
शैडोचैसर

1
क्या यह अभी भी काम करता है अगर आप केवल विभाजन की गणना करते हैं और आंतरिक गणना करने योग्य नहीं हैं? चूँकि भीतर का एन्यूमरेटर टाल दिया जाता है, तब तक भीतर का कोई भी कोड (करंट से नहीं) तब तक क्रियान्वित होता है, जब तक कि इसे एन्यूमरेट नहीं किया जाता है इसलिए मूवमेंट () केवल बाहरी विभाजन फ़ंक्शन द्वारा बुलाया जाएगा, है ना? अगर मेरी धारणा सही है, तो इससे संभावित रूप से मूल तत्वों में n तत्वों के साथ n विभाजन हो सकता है और भीतरी गणनाओं में अप्रत्याशित परिणाम मिलेंगे
ब्रैड

@Brad यह "असफल" होगा जैसा कि आप उम्मीद, इस सूत्र में मुद्दों में से कुछ के लिए इसी तरह के रूप में stackoverflow.com/questions/419019/... (विशेष रूप से stackoverflow.com/a/20953521/1037948 )
drzaus

4

मैं इसका उपयोग करता हूं:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> instance, int partitionSize)
{
    return instance
        .Select((value, index) => new { Index = index, Value = value })
        .GroupBy(i => i.Index / partitionSize)
        .Select(i => i.Select(i2 => i2.Value));
}

कृपया समझाएं। मैं बिना किसी परेशानी के इस फ़ंक्शन का उपयोग कर रहा हूं!
एल्मर

प्रश्न को फिर से पढ़ें और देखें कि क्या आपको अपने फ़ंक्शन के साथ n (लगभग) समान लंबाई वाले भाग मिलते हैं
मुहम्मद हसन खान

@ आपका उत्तर सही है, लेकिन प्रश्न इसके लिए गलत है। आपका जवाब प्रत्येक चंक के लिए निश्चित आकार के साथ अज्ञात संख्या देता है (बिल्कुल विभाजन के रूप में, इसके लिए आपने जो नाम दिया है)। लेकिन ओपी एक स्प्लिट फंक्शनलिटी चाहता है, जहां यह किसी भी आकार के साथ चंक की निश्चित संख्या देता है (उम्मीद है कि समान या समान आकार के करीब)। शायद यहां अधिक अनुकूल है stackoverflow.com/questions/3773403/…
nawfal

मुझे लगता है कि आप सिर्फ i.Index / विभाजन को बदल सकते हैं। I। Iex% विभाजन को आकार दें और अनुरोधित परिणाम प्राप्त करें। मैं इसे स्वीकार किए गए उत्तर पर भी पसंद करता हूं क्योंकि यह अधिक कॉम्पैक्ट और पठनीय है।
जेक ड्र्यू

2

यह स्मृति कुशल है और जितना संभव हो उतना दोषपूर्ण निष्पादन (प्रति बैच) और रैखिक समय में संचालित होता है O (n)

    public static IEnumerable<IEnumerable<T>> InBatchesOf<T>(this IEnumerable<T> items, int batchSize)
    {
        List<T> batch = new List<T>(batchSize);
        foreach (var item in items)
        {
            batch.Add(item);

            if (batch.Count >= batchSize)
            {
                yield return batch;
                batch = new List<T>();
            }
        }

        if (batch.Count != 0)
        {
            //can't be batch size or would've yielded above
            batch.TrimExcess();
            yield return batch;
        }
    }

2

इस प्रश्न (और इसके चचेरे भाई) के लिए बहुत सारे शानदार उत्तर हैं। मुझे स्वयं इसकी आवश्यकता थी और एक ऐसा समाधान तैयार किया था, जो एक ऐसे परिदृश्य में कुशल और त्रुटि सहिष्णु होने के लिए डिज़ाइन किया गया हो, जहां स्रोत संग्रह को सूची के रूप में माना जा सकता है। यह किसी भी आलसी पुनरावृत्ति का उपयोग नहीं करता है इसलिए यह अज्ञात आकार के संग्रह के लिए उपयुक्त नहीं हो सकता है जो स्मृति दबाव लागू कर सकता है।

static public IList<T[]> GetChunks<T>(this IEnumerable<T> source, int batchsize)
{
    IList<T[]> result = null;
    if (source != null && batchsize > 0)
    {
        var list = source as List<T> ?? source.ToList();
        if (list.Count > 0)
        {
            result = new List<T[]>();
            for (var index = 0; index < list.Count; index += batchsize)
            {
                var rangesize = Math.Min(batchsize, list.Count - index);
                result.Add(list.GetRange(index, rangesize).ToArray());
            }
        }
    }
    return result ?? Enumerable.Empty<T[]>().ToList();
}

static public void TestGetChunks()
{
    var ids = Enumerable.Range(1, 163).Select(i => i.ToString());
    foreach (var chunk in ids.GetChunks(20))
    {
        Console.WriteLine("[{0}]", String.Join(",", chunk));
    }
}

मैंने सवालों के इस परिवार में कुछ जवाब देखे हैं जो GetRange और Math.Min का उपयोग करते हैं। लेकिन मेरा मानना ​​है कि कुल मिलाकर यह त्रुटि जाँच और दक्षता के मामले में एक अधिक संपूर्ण समाधान है।


1
   protected List<List<int>> MySplit(int MaxNumber, int Divider)
        {
            List<List<int>> lst = new List<List<int>>();
            int ListCount = 0;
            int d = MaxNumber / Divider;
            lst.Add(new List<int>());
            for (int i = 1; i <= MaxNumber; i++)
            {
                lst[ListCount].Add(i);
                if (i != 0 && i % d == 0)
                {
                    ListCount++;
                    d += MaxNumber / Divider;
                    lst.Add(new List<int>());
                }
            }
            return lst;
        }

1

महान उत्तर, मेरे परिदृश्य के लिए मैंने स्वीकार किए गए उत्तर का परीक्षण किया, और ऐसा लगता है कि यह आदेश नहीं रखता है। वहाँ भी नवाफ द्वारा महान जवाब है जो आदेश रखता है। लेकिन मेरे परिदृश्य में मैं शेष को सामान्य तरीके से विभाजित करना चाहता था, मैंने देखा कि सभी उत्तर शेष या शुरुआत में या अंत में फैल गए।

मेरा जवाब शेष शेष को भी सामान्यीकृत तरीके से फैलाना है।

 static class Program
{          
    static void Main(string[] args)
    {
        var input = new List<String>();
        for (int k = 0; k < 18; ++k)
        {
            input.Add(k.ToString());
        }
        var result = splitListIntoSmallerLists(input, 15);            
        int i = 0;
        foreach(var resul in result){
            Console.WriteLine("------Segment:" + i.ToString() + "--------");
            foreach(var res in resul){
                Console.WriteLine(res);
            }
            i++;
        }
        Console.ReadLine();
    }

    private static List<List<T>> splitListIntoSmallerLists<T>(List<T> i_bigList,int i_numberOfSmallerLists)
    {
        if (i_numberOfSmallerLists <= 0)
            throw new ArgumentOutOfRangeException("Illegal value of numberOfSmallLists");

        int normalizedSpreadRemainderCounter = 0;
        int normalizedSpreadNumber = 0;
        //e.g 7 /5 > 0 ==> output size is 5 , 2 /5 < 0 ==> output is 2          
        int minimumNumberOfPartsInEachSmallerList = i_bigList.Count / i_numberOfSmallerLists;                        
        int remainder = i_bigList.Count % i_numberOfSmallerLists;
        int outputSize = minimumNumberOfPartsInEachSmallerList > 0 ? i_numberOfSmallerLists : remainder;
        //In case remainder > 0 we want to spread the remainder equally between the others         
        if (remainder > 0)
        {
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                normalizedSpreadNumber = (int)Math.Floor((double)i_numberOfSmallerLists / remainder);    
            }
            else
            {
                normalizedSpreadNumber = 1;
            }   
        }
        List<List<T>> retVal = new List<List<T>>(outputSize);
        int inputIndex = 0;            
        for (int i = 0; i < outputSize; ++i)
        {
            retVal.Add(new List<T>());
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                retVal[i].AddRange(i_bigList.GetRange(inputIndex, minimumNumberOfPartsInEachSmallerList));
                inputIndex += minimumNumberOfPartsInEachSmallerList;
            }
            //If we have remainder take one from it, if our counter is equal to normalizedSpreadNumber.
            if (remainder > 0)
            {
                if (normalizedSpreadRemainderCounter == normalizedSpreadNumber-1)
                {
                    retVal[i].Add(i_bigList[inputIndex]);
                    remainder--;
                    inputIndex++;
                    normalizedSpreadRemainderCounter=0;
                }
                else
                {
                    normalizedSpreadRemainderCounter++;
                }
            }
        }
        return retVal;
    }      

}

0

यदि इन भागों में आदेश बहुत महत्वपूर्ण नहीं है, तो आप यह कोशिश कर सकते हैं:

int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int n = 3;

var result =
   array.Select((value, index) => new { Value = value, Index = index }).GroupBy(i => i.Index % n, i => i.Value);

// or
var result2 =
   from i in array.Select((value, index) => new { Value = value, Index = index })
   group i.Value by i.Index % n into g
   select g;

हालांकि इनको किसी कारण से IEnumerable <IEnumerable <int >> पर नहीं डाला जा सकता है ...


यह किया जा सकता है। डायरेक्ट कास्टिंग के बजाय बस फंक्शन को सामान्य बनाएं और फिर इसे अपने इंट सरणी के लिए कॉल करें
nawfal

0

यह मेरा कोड है, अच्छा और छोटा है।

 <Extension()> Public Function Chunk(Of T)(ByVal this As IList(Of T), ByVal size As Integer) As List(Of List(Of T))
     Dim result As New List(Of List(Of T))
     For i = 0 To CInt(Math.Ceiling(this.Count / size)) - 1
         result.Add(New List(Of T)(this.GetRange(i * size, Math.Min(size, this.Count - (i * size)))))
     Next
     Return result
 End Function

0

यह मेरा तरीका है, आइटम की सूची बनाना और स्तंभों द्वारा पंक्ति को तोड़ना

  int repat_count=4;

  arrItems.ForEach((x, i) => {
    if (i % repat_count == 0) 
        row = tbo.NewElement(el_tr, cls_min_height);
    var td = row.NewElement(el_td);
    td.innerHTML = x.Name;
  });

0

मैं स्ट्रिंग के साथ एक की तरह एक विभाजन की तलाश में था, इसलिए पूरी सूची को कुछ नियम के अनुसार विभाजित किया गया है, न केवल पहला भाग, यह मेरा समाधान है

List<int> sequence = new List<int>();
for (int i = 0; i < 2000; i++)
{
     sequence.Add(i);
}
int splitIndex = 900;
List<List<int>> splitted = new List<List<int>>();
while (sequence.Count != 0)
{
    splitted.Add(sequence.Take(splitIndex).ToList() );
    sequence.RemoveRange(0, Math.Min(splitIndex, sequence.Count));
}

अगली बार कोशिश करें: var nrs = Enumerable.Range (1,2000) .ToList ();
एमबीरोएस

0

यहाँ भागों की संख्या के बजाय आइटम की संख्या के लिए थोड़ा ट्वीक है:

public static class MiscExctensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int nbItems)
    {
        return (
            list
            .Select((o, n) => new { o, n })
            .GroupBy(g => (int)(g.n / nbItems))
            .Select(g => g.Select(x => x.o))
        );
    }
}

-1
int[] items = new int[] { 0,1,2,3,4,5,6,7,8,9, 10 };

int itemIndex = 0;
int groupSize = 2;
int nextGroup = groupSize;

var seqItems = from aItem in items
               group aItem by 
                            (itemIndex++ < nextGroup) 
                            ? 
                            nextGroup / groupSize
                            :
                            (nextGroup += groupSize) / groupSize
                            into itemGroup
               select itemGroup.AsEnumerable();

-1

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

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

using System.Collections.Generic;

public static class EnumerableExtensions
{
    /// <summary>
    /// Partitions an enumerable into individual pages of a specified size, still scanning the source enumerable just once
    /// </summary>
    /// <typeparam name="T">The element type</typeparam>
    /// <param name="enumerable">The source enumerable</param>
    /// <param name="pageSize">The number of elements to return in each page</param>
    /// <returns></returns>
    public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int pageSize)
    {
        var enumerator = enumerable.GetEnumerator();

        while (enumerator.MoveNext())
        {
            var indexWithinPage = new IntByRef { Value = 0 };

            yield return SubPartition(enumerator, pageSize, indexWithinPage);

            // Continue iterating through any remaining items in the page, to align with the start of the next page
            for (; indexWithinPage.Value < pageSize; indexWithinPage.Value++)
            {
                if (!enumerator.MoveNext())
                {
                    yield break;
                }
            }
        }
    }

    private static IEnumerable<T> SubPartition<T>(IEnumerator<T> enumerator, int pageSize, IntByRef index)
    {
        for (; index.Value < pageSize; index.Value++)
        {
            yield return enumerator.Current;

            if (!enumerator.MoveNext())
            {
                yield break;
            }
        }
    }

    private class IntByRef
    {
        public int Value { get; set; }
    }
}

यह बिल्कुल काम नहीं कर रहा है! सबसे अच्छा संभव है यहां stackoverflow.com/questions/13709626/… ! टिप्पणी देखो।
सालिएंट्रेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.