লিনকু সহ সাবলিস্টে তালিকা বিভক্ত করুন


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
গ্রুপবাই একটি অন্তর্নিহিত বাছাই করে। যা পারফরম্যান্সকে মেরে ফেলতে পারে। আমাদের যা দরকার তা হল সিলেক্টম্যানির একরকম বিপরীত।
yfeldblum

5
@ জাস্টিস, গ্রুপবাই হ্যাশিংয়ের মাধ্যমে প্রয়োগ করা যেতে পারে। আপনি কীভাবে জানবেন যে গ্রুপবাইয়ের বাস্তবায়ন "পারফরম্যান্সকে হত্যা করতে পারে"?
অ্যামি বি

5
গোষ্ঠী দ্বারা সমস্ত উপাদানগুলি গণনা করা না হওয়া পর্যন্ত কোনও কিছুই ফেরত দেয় না। এই কারণেই এটি ধীর। ওপি চাইলে তালিকাগুলি সুসংগত, সুতরাং আরও ভাল [a,g,e]কোনও মৌলিক তালিকাকে তালিকাভুক্ত করার আগে প্রথম উপ তালিকাটি উপস্থাপন করতে পারে।
কর্নেল আতঙ্ক

9
অসীম আইএনউমারেবলের চূড়ান্ত উদাহরণটি ধরুন। GroupBy(x=>f(x)).First()কখনও একটি গ্রুপ উত্পাদন করবে না। ওপি তালিকাগুলি সম্পর্কে জিজ্ঞাসা করেছিল, তবে আমরা যদি কেবলমাত্র একটি একক পুনরাবৃত্তি তৈরি করে আইইনামারবেবলের সাথে কাজ করতে লিখি, আমরা পারফরম্যান্সের সুবিধাটি কাটাতে পারি।
কর্নেল আতঙ্ক

8
@ নিক অর্ডার আপনার উপায় সংরক্ষণ করা হয় না যদিও। এটি এখনও জেনে রাখা ভাল জিনিস তবে আপনি সেগুলি (0,3,6,9, ...), (1,4,7,10, ...), (2,5,8) এ ভাগ করে নিচ্ছেন , 11, ...)। যদি অর্ডারটি গুরুত্বপূর্ণ না হয় তবে তা ঠিক আছে তবে এই ক্ষেত্রে এটির মতো মনে হচ্ছে।
রেফেক্সাস

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");
mroach

10
আমি এটি পছন্দ করি তবে এটি অত্যন্ত দক্ষ নয়
স্যাম জাফরন

51
আমি এই এক পছন্দ কিন্তু সময় দক্ষতা হয় O(n²)। আপনি তালিকার মাধ্যমে পুনরাবৃত্তি করতে পারেন এবং একটি O(n)সময় পেতে পারেন ।
hIpPy

8
@hIpPy, এটি কেমন n 2? আমার কাছে রৈখিক দেখাচ্ছে
বিবেক মহারাজ

13
@ বিবেকমহরজ প্রতিবার sourceমোড়ানো দ্বারা প্রতিস্থাপিত হয়েছে IEnumerable। উপাদান গ্রহণ থেকে তাই sourceকয়েক পরত মাধ্যমে যায় Skipগুলি
Lasse Espeholt

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

যা বিশাল কল চেইন এড়াতে পারবে। তবুও, এই পদ্ধতির একটি সাধারণ ত্রুটি রয়েছে। এটি চলার চেষ্টাটি ইস্যুটি হাইলাইট করার জন্য প্রতি অংশের জন্য দুটি গণনা তৈরি করে:

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

এটিকে কাটিয়ে উঠতে আমরা ক্যামেরনের পদ্ধতির চেষ্টা করতে পারি , এটি উপরের পরীক্ষায় উড়ন্ত রঙগুলিতে পাস করে কারণ এটি কেবল একবার গণনাটি চালায়।

সমস্যাটি হ'ল এর আলাদা ত্রুটি রয়েছে, এটি প্রতিটি অংশের প্রতিটি আইটেমকে রূপায়িত করে approach

দৌড়াতে চেষ্টা করুন তা বোঝাতে:

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


ত্রুটিটি গণনাকারী হবে anষুধ (0, 100) h .ToArray () একটি ব্যতিক্রম ছুঁড়ে?
ক্যামেরন ম্যাকফারল্যান্ড

@ সামস্যাফ্রন আমি আমার উত্তরটি আপডেট করেছি এবং কোডটি কীভাবে আমি বিশিষ্ট ব্যবহারের ক্ষেত্রে মনে হচ্ছে তার জন্য কোডটি অত্যন্ত সরল করে দিয়েছি (এবং সতর্কতাগুলি স্বীকার করুন)।
CasperOne

আইকিউয়ারেবল <> চয়ন করার বিষয়ে কী? আমার অনুমান যে একটি নিতে / এড়িয়ে পদ্ধতির অনুকূল হবে যদি আমরা প্রদানকারীর কাছে অপারেশনের সর্বোচ্চ প্রতিনিধির নিকট হস্তান্তরের চাই
Guillaume86

@ গিলাইউমূম I৮ আমি সম্মত হলাম, আপনার যদি আইলিস্ট বা আইকিউয়েরেবল থাকে তবে আপনি এমন সমস্ত ধরণের শর্টকাট নিতে পারেন যা এটি আরও দ্রুত করে তুলতে পারে (লিনক অন্যান্য পদ্ধতিগুলির জন্য অভ্যন্তরীণভাবে এটি করে)
স্যাম

1
দক্ষতার জন্য এটি এখন পর্যন্ত সেরা উত্তর। আমি স্ক্লালবুলকপিটি আইইনুমারেবলের সাথে ব্যবহার করে একটি সমস্যা পেয়েছি যা প্রতিটি কলামে অতিরিক্ত প্রক্রিয়া চালায়, সুতরাং এটি অবশ্যই কেবল একটি পাস দিয়ে দক্ষতার সাথে চালানো উচিত। এটি আমাকে পরিচালনযোগ্য আকারের অংশগুলিতে আইয়ুনামেবলকে ছিন্ন করতে অনুমতি দেবে। (যারা ভাবছেন তাদের জন্য, আমি স্কেলবুল্ককপির স্ট্রিমিং মোড সক্ষম করেছিলাম, যা মনে হচ্ছে এটি ভেঙে গেছে)।
Brain2000

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

তারপরে আপনি এটিকে কল করতে পারেন এবং এটি লিনকিউ সক্ষম হয়েছে যাতে ফলস্বরূপ ক্রমগুলির উপর আপনি অন্যান্য ক্রিয়াকলাপ করতে পারেন।


স্যামের উত্তরের আলোকে , আমি অনুভব করেছি যে এটি করা ছাড়া আরও সহজ উপায় ছিল:

  • আবার তালিকার মাধ্যমে আইট্রেট করা (যা আমি মূলত করিনি)
  • অংশগুলি প্রকাশের আগে দলগুলিতে আইটেমগুলিকে ধাতবকরণ (বড় আকারের আইটেমগুলির জন্য, মেমরির সমস্যা থাকবে)
  • স্যাম পোস্ট করা সমস্ত কোড

তাই বলা হয়, এখানে অন্য পাস, যা আমি করতে একটি এক্সটেনশন পদ্ধতিতে বিধিবদ্ধ করেছি 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দ্বিতীয়বার বলা হয়, তখনও ডকুমেন্টেশন (জোর দেওয়া খনি) অনুসারে এটি মিথ্যা ফিরবে :

যদি মুভনেক্সট সংগ্রহের শেষ প্রান্তটি অতিক্রম করে তবে সংগ্রহের শেষ উপাদানটির পরে গণকের অবস্থান নির্ধারণ করা হবে এবং মুভ নেক্সট মিথ্যা প্রত্যাবর্তন করবে। যখন গণক এই অবস্থানে থাকে তখন মুভি নেক্সটে পরবর্তী কলগুলিও রিসেট না বলা পর্যন্ত মিথ্যা ফিরিয়ে দেয়।

এই মুহুর্তে, লুপটি ভেঙে যাবে এবং ক্রমগুলির ক্রমটি শেষ হবে।

এটি একটি সহজ পরীক্ষা:

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,

একটি গুরুত্বপূর্ণ দ্রষ্টব্য, আপনি যদি পুরো সন্তানের ক্রমটি নিকাশ না করেন বা পিতামাতার ক্রমের কোনও বিন্দুতে ভাঙ্গেন না তবে এটি কাজ করবে না । এটি একটি গুরুত্বপূর্ণ সতর্কীকরণ, কিন্তু যদি আপনার ব্যবহার কেস যে আপনার গ্রাস করবে যে সিকোয়েন্সগুলির ক্রমের উপাদান তবে এটি আপনার পক্ষে কাজ করবে।

অতিরিক্তভাবে, আপনি যদি অর্ডার নিয়ে খেলেন তবে এটি অদ্ভুত জিনিসগুলি করবে, যেমন স্যামের এক পর্যায়ে হয়েছিল


আমি মনে করি এটিই সেরা সমাধান ... একমাত্র সমস্যা হ'ল তালিকার দৈর্ঘ্য নেই ... এটির গণনা রয়েছে। তবে এটি পরিবর্তন করা সহজ। আমরা এমনকি তালিকা তৈরি না করে অফসেট / দৈর্ঘ্যের সংমিশ্রণ সহ মূল তালিকার রেফারেন্স সম্বলিত আইমনামেবলগুলি ফিরিয়েই এটি আরও ভাল করে তুলতে পারি। সুতরাং, যদি গ্রুপ আকার বড় হয়, আমরা স্মৃতি নষ্ট করি না। আপনি যদি এটি লিখতে চান তবে মন্তব্য করুন।
আমির

@ আমির আমি লিখিত লেখাটি দেখতে চাই
সানডমুর

এটি দুর্দান্ত এবং দ্রুত - ক্যামেরন আপনার পরেও খুব অনুরূপ একটি পোস্ট করেছে, কেবলমাত্র সতর্কতা হ'ল এটি খণ্ডগুলিকে বাফার করে, যদি খণ্ডগুলি এবং আইটেমের আকারগুলি বড় হয় তবে স্মৃতি বহির্ভূত হতে পারে। বিকল্প হিসাবে আমার উত্তর দেখুন, অনেক বেশি কেশিক হলেও উত্তর দিন answer
স্যাম জাফরন

@ সামস্যাফ্রন হ্যাঁ, যদি আপনার কাছে প্রচুর পরিমাণে আইটেম থাকে তবে List<T>আপনি সম্ভবত বাফারিংয়ের কারণে স্মৃতিযুক্ত সমস্যা নিয়ে চলেছেন। পূর্ববর্তী ক্ষেত্রে, আমার জবাবটি লক্ষ্য করা উচিত ছিল, তবে মনে হয়েছিল যে সময়ে ফোকাসটি অনেকগুলি পুনরাবৃত্তির দিকে ছিল। এটি বলেছিল, আপনার সমাধানটি আসলে চুলচেরা। আমি এটি পরীক্ষা করিনি, তবে এখন এটি ভাবছে যে এর চেয়ে কম লোমযুক্ত সমাধান রয়েছে কিনা।
CasperOne

@ ক্যাস্পারওন হ্যাঁ ... আমি যখন এই সংখ্যাটি পৃথক করার জন্য কোন উপায় অনুসন্ধান করছিলাম তখন গুগল আমাকে এই পৃষ্ঠাটি দিয়েছিল, আমার নির্দিষ্ট ব্যবহারের ক্ষেত্রে আমি ডিবি থেকে প্রত্যাবর্তিত রেকর্ডগুলির একটি অত্যন্ত বড় তালিকা বিভক্ত করছি, যদি আমি সেগুলি বাস্তবায়িত করি তবে এটি ফুঁ দিয়ে উঠবে (প্রকৃতপক্ষে ড্যাপারের একটি বাফার রয়েছে: কেবল এই ব্যবহারের ক্ষেত্রে ভুয়া বিকল্প)
স্যাম জাফরন

48

ঠিক আছে, আমি এখানে এটি গ্রহণ:

  • সম্পূর্ণ অলস: অসীম গণনার উপর কাজ করে
  • অন্তর্বর্তী কোনও অনুলিপি / বাফারিং নেই
  • ও (এন) কার্যকর করার সময়
  • অভ্যন্তরীণ সিকোয়েন্সগুলি কেবল আংশিকভাবে গ্রাস করা হয় এমন সময়েও কাজ করে

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()return টি ফিরে আসবে)।

দ্রষ্টব্য: @ আওলসোউকা দ্বারা নির্দেশিত ত্রুটিগুলি সমাধান করার জন্য উত্তরটি আপডেট করা হয়েছে।


2
খুব সুন্দর. আমার "সঠিক" সমাধানটি এর চেয়ে জটিল ছিল। এটি # 1 উত্তর আইএমএইচও।
কেসিবি

যখন ToArray () ডাকা হয় তখন এটি অপ্রত্যাশিত (একটি API স্ট্যান্ডপয়েন্ট থেকে) আচরণে ভোগে, এটি থ্রেড-নিরাপদও নয়।
আওলসোওকা

@ আওলজোউকা: আপনি কি দয়া করে বিস্তারিত বলতে পারবেন?
ডি গ্র্যাবার

@ 3 ডিগ্রাবার সম্ভবত এটিই ছিল যে আমি আপনার কোডটিকে পুনঃসংশ্লিষ্ট করেছিলাম (দুঃখিত, এটি উত্স থেকে কিছুটা দীর্ঘ অতীত হয়ে উঠেছে, মূলত আমি উত্সরেখাতে পাস হওয়া কোনও এক্সটেনশন পদ্ধতির পরিবর্তে)। আমি যে পরীক্ষার কেসটি ব্যবহার করেছি তা এই প্রভাবের জন্য কিছু ছিল: int [] অ্যারেটিসোর্ট = নতুন ইনট [] {9, 7, 2, 6, 3, 4, 8, 5, 1, 10, 11, 12, 13}; var উত্স = Chunkify <int> (অ্যারেটিসোর্ট, 3)। টোআররে (); উত্সটিতে প্রতিক্রিয়া জানানো হয়েছে যে 13 টি অংশ ছিল (উপাদানগুলির সংখ্যা)। এটি আমার কাছে বোধগম্য হয়েছিল যতক্ষণ না আপনি অভ্যন্তরীণ সূত্রগুলি অনুসন্ধান করেন যদি না গণকের বৃদ্ধি হয় না।
আওলসোওকা

1
@ আওলজোউকা: খুব বৈধ পয়েন্ট। আমি একটি সতর্কতা এবং একটি ব্যবহার বিভাগ যুক্ত করেছি। কোডটি ধরে নিয়েছে যে আপনি অভ্যন্তরীন গণনার তুলনায় পুনরাবৃত্তি করতে পারেন। আপনার সমাধানের সাথে আপনি যদিও অলসতা বাজেয়াপ্ত করেন। আমি মনে করি আইমনুমরেটর কে ক্যাচ দিয়ে একটি কাস্টম দিয়ে উভয় বিশ্বের সেরা পাওয়া সম্ভব হবে। যদি আমি কোনও সমাধান খুঁজে পাই তবে আমি এটি এখানে পোস্ট করব ...
ডি গ্রাবার 20'14

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
@CaseyB উল্লেখ করা হয়েছে, একই ব্যর্থ থেকে এই ভুগছেন 3dGrabber এখানে সম্বোধন stackoverflow.com/a/20953521/1037948 , কিন্তু মানুষ এটা দ্রুত!
ড্রজাউস

1
এটি একটি সুন্দর সমাধান। এটি প্রতিশ্রুতি দেয় ঠিক কি করে।
রড হার্টজেল

এখন পর্যন্ত সবচেয়ে মার্জিত এবং পয়েন্ট সমাধান। কেবলমাত্র এটি হ'ল আপনার নেতিবাচক সংখ্যার জন্য একটি চেক যোগ করা উচিত, এবং একটি আর্গুমেন্ট
এক্সসেপশন

13

আমি মনে করি নিম্নলিখিত পরামর্শটি দ্রুত হবে। অ্যারে.কপি ব্যবহারের দক্ষতার জন্য আমি উত্সের অলসতার ত্যাগ করছি এবং আমার প্রতিটি সাবলিস্টের দৈর্ঘ্য সময়ের আগে জেনেছি।

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

নেই শুধু দ্রুততম, এটি সঠিকভাবে আরও গণনীয় অপারেশন ফলাফলে পরিচালনা, অর্থাত্ items.Chunk (5) .Reverse () SelectMany (এক্স => এক্স)।
খুব

9

আমরা সত্যিকারের অলস মূল্যায়ন করার জন্য @ জারেডপারের সমাধানটিকে উন্নত করতে পারি। আমরা একটি GroupAdjacentByপদ্ধতি ব্যবহার করি যা একই কী দিয়ে একটানা উপাদানগুলির গোষ্ঠী দেয়:

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

গ্রুপগুলি একের পর এক ফলিত হয়েছে, এই সমাধানটি দীর্ঘ বা অসীম সিকোয়েন্সগুলির সাথে দক্ষতার সাথে কাজ করে।


8

আমি বেশ কয়েক বছর আগে একটি ক্লাম্প এক্সটেনশন পদ্ধতি লিখেছিলাম। দুর্দান্ত কাজ করে এবং এটি এখানে দ্রুত বাস্তবায়ন। : P: P

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

এটি কাজ করা উচিত তবে এটি শতভাগ অংশকে বাফার করছে, আমি এড়াতে চাইছিলাম ... তবে এটি অবিশ্বাস্যভাবে লোমশ হয়ে গেছে।
স্যাম জাফরন

@ সামস্যাফ্রন ইয়েপ বিশেষত যদি আপনি প্লিনক জাতীয় জিনিসগুলিকে মিশ্রণে ফেলে দেন তবে আমার বাস্তবায়নটি মূলত এর জন্য।
ক্যামেরন ম্যাকফারল্যান্ড

আমার উত্তরটি প্রসারিত করুন, আপনি কী ভাবছেন তা আমাকে জানাতে দিন
স্যাম জাফরন

@ ক্যামেরনম্যাকফারল্যান্ড - আপনি কি ব্যাখ্যা করতে পারবেন কেন গণনার জন্য দ্বিতীয় চেক == আকারের প্রয়োজন? ধন্যবাদ।
dugas

8

System.Interactive প্রদান করে Buffer()এই কাজের জন্য। কিছু দ্রুত পরীক্ষণ দেখায় পারফরম্যান্স স্যামের সমাধানের সাথে সমান।


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

আমি যতদূর জানি, গেটরেঞ্জ () নেওয়া আইটেমের সংখ্যার ক্ষেত্রে লিনিয়ার। সুতরাং এটি ভাল সঞ্চালন করা উচিত।


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। তবে আমি দুটি জিনিস সুপারিশ করি ১। ব্লক ব্যবহারের পরিবর্তে ফরচ ব্যবহার করুন। ২. তালিকার কনস্ট্রাক্টারে চুনক সাইজ পাস করুন যাতে তালিকাটি তার সর্বাধিক প্রত্যাশিত আকারটি জানে।
উসমান জাফর

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 বছর আগে পোস্ট করার পরিবর্তে পোস্ট করা হয়েছিল ny ।
রবার্ট ম্যাককি

3

যদি তালিকাটি সিস্টেমের থাকে .c সংগ্রহগুলি ge আপনি অনুলিপি করতে প্রাথমিক উপাদান এবং উপাদানগুলির সংখ্যা নির্দিষ্ট করেছেন।

আপনি নিজের আসল তালিকার 3 টি ক্লোন তৈরি করতে এবং নিজের পছন্দ অনুযায়ী আকার সঙ্কুচিত করতে প্রতিটি তালিকার "সরানরেঞ্জ" ব্যবহার করতে পারেন।

বা এটি আপনার জন্য কেবল একটি সহায়ক পদ্ধতি তৈরি করুন।


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
আমি ব্যবহার করা একটি পদ্ধতির সাথে খুব অনুরূপ, তবে আমি প্রস্তাব করি যে উত্সটি অনুমেয় নয়। উদাহরণস্বরূপ, যদি উত্স যদি কোনও লিনকিউ ক্যোয়ারির ফলাফল হয় তবে এড়িয়ে যান / নিন কিউয়ের এনবিচঙ্ক এনামিরেশনগুলিকে ট্রিগার করবে। ব্যয়বহুল পেতে পারে। IList বা আইকোলিকেশনটি উত্সের ধরণ হিসাবে ব্যবহার করা ভাল। যা সমস্যাটি পুরোপুরি এড়িয়ে চলে।
আরবি ডেভিডসন

2

প্যাকেজযুক্ত / রক্ষণাবেক্ষণ সমাধানে আগ্রহী যে কোনও ব্যক্তির জন্য, মোরিলিংক লাইব্রেরি Batchএক্সটেনশন পদ্ধতি সরবরাহ করে যা আপনার অনুরোধ করা আচরণের সাথে মেলে:

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

Batchবাস্তবায়ন অনুরূপ ক্যামেরন MacFarland এর উত্তর , ফেরার আগে খণ্ড / ব্যাচ রূপান্তর জন্য একটি জমিদার যোগে সঙ্গে, এবং সঞ্চালিত বেশ ভাল।


এটি গ্রহণযোগ্য উত্তর হওয়া উচিত। চাকাটি পুনরায় উদ্ভাবন করার পরিবর্তে মোড়লিংক ব্যবহার করা উচিত
ওতাবেক খোলিকভ

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

আর একটি উপায় হ'ল আরএক্স বাফার অপারেটর ব্যবহার করা

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

আইএমএইচও সবচেয়ে উত্তর দিক।
স্টানিস্লাভ বারকভ

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

তবে আসলে আমি লিনক ছাড়াই সংশ্লিষ্ট পদ্ধতি লিখতে পছন্দ করব।


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

আমার দুটি সেন্ট Toোকাতে ...

উত্সটি ছিন্ন করতে তালিকার ধরণটি ব্যবহার করে আমি আরও একটি খুব কমপ্যাক্ট সমাধান পেয়েছি:

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;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.