সি # তে গণনার সময় কোনও তালিকা <টি> থেকে আইটেমগুলি সরানোর বুদ্ধিমান উপায়


87

আমার কাছে সংগ্রহ থেকে কোনও আইটেম লুপে গণনার সময় সরিয়ে ফেলার চেষ্টা করার ক্লাসিক কেস রয়েছে:

List<int> myIntCollection = new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);

foreach (int i in myIntCollection)
{
    if (i == 42)
        myIntCollection.Remove(96);    // The error is here.
    if (i == 25)
        myIntCollection.Remove(42);    // The error is here.
}

পরিবর্তন হওয়ার পরে পুনরাবৃত্তির শুরুতে একটি InvalidOperationExceptionনিক্ষেপ করা হয়, কারণ অন্তর্নিহিত সংগ্রহ পরিবর্তনের সময় গণকগণ পছন্দ করেন না।

পুনরাবৃত্তি করার সময় আমাকে সংগ্রহে পরিবর্তন করতে হবে। এটি এড়ানোর জন্য অনেকগুলি নিদর্শন ব্যবহার করা যেতে পারে তবে এগুলির কোনওটিরই ভাল সমাধান বলে মনে হয় না:

  1. এই লুপটির ভিতরে মুছবেন না, পরিবর্তে একটি পৃথক "মুছুন তালিকা" রাখুন, যা আপনি প্রধান লুপের পরে প্রক্রিয়া করেন।

    এটি সাধারণত একটি ভাল সমাধান, তবে আমার ক্ষেত্রে, আইটেমটি সত্যই আইটেমটি মোছার জন্য লুপের পরে অবধি ততক্ষণে "অপেক্ষা" হিসাবে চলে যেতে হবে যা আমার কোডের লজিক প্রবাহকে পরিবর্তন করে।

  2. আইটেমটি মোছার পরিবর্তে কেবল আইটেমটিতে একটি পতাকা সেট করুন এবং এটি নিষ্ক্রিয় হিসাবে চিহ্নিত করুন। তারপরে তালিকাটি পরিষ্কার করতে প্যাটার্ন 1 এর কার্যকারিতা যুক্ত করুন।

    এটি আমার সমস্ত প্রয়োজনের জন্য কাজ করবে , তবে এর অর্থ হ'ল প্রতি বার যখন কোনও আইটেম অ্যাক্সেস করা হয় তখন নিষ্ক্রিয় পতাকাটি পরীক্ষা করতে প্রচুর কোড পরিবর্তন করতে হবে। এটি আমার পছন্দ অনুসারে অনেক বেশি প্রশাসন।

  3. একরকম যে থেকে আহরিত কোন ক্লাসে প্যাটার্ন 2 ধারনা নিগমবদ্ধ List<T>। এই সুপারলিস্টটি নিষ্ক্রিয় পতাকা, সত্যের পরে অবজেক্টগুলির মোছা এবং গণনার গ্রাহকদের নিষ্ক্রিয় হিসাবে চিহ্নিত আইটেমগুলি প্রকাশ করবে না। মূলত, এটি কেবল প্যাটার্ন 2 (এবং পরবর্তীকালে প্যাটার্ন 1) এর সমস্ত ধারণাকে আবদ্ধ করে।

    এই মত একটি বর্গ কি বিদ্যমান? কারও কি এর জন্য কোড আছে? নাকি এর চেয়ে ভাল উপায় আছে?

  4. আমাকে বলা হয়েছে যে myIntCollection.ToArray()পরিবর্তে অ্যাক্সেস করা myIntCollectionসমস্যার সমাধান করবে এবং আমাকে লুপের ভিতরে মুছতে দেবে।

    এটি আমার কাছে খারাপ ডিজাইনের প্যাটার্নের মতো বলে মনে হচ্ছে, না ঠিক আছে?

বিশদ:

  • তালিকায় অনেকগুলি আইটেম থাকবে এবং আমি কেবল সেগুলি থেকে কিছু সরিয়ে ফেলব।

  • লুপের অভ্যন্তরে আমি সমস্ত ধরণের প্রক্রিয়া করব, যোগ করা, অপসারণ ইত্যাদি করব, সুতরাং সমাধানটি মোটামুটি জেনেরিক হওয়া দরকার।

  • আমার যে আইটেমটি মুছতে হবে তা লুপের বর্তমান আইটেম নাও হতে পারে। উদাহরণস্বরূপ, আমি একটি 30 আইটেম লুপের আইটেম 10 এ থাকতে পারি এবং আইটেম 6 বা 26 আইটেমটি সরিয়ে ফেলতে হবে the অ্যারের মাধ্যমে পিছনে হাঁটা আর এর কারণে আর কাজ করবে না। ; ও (


অন্য কারও জন্য সম্ভাব্য দরকারী তথ্য: সংগ্রহ এড়ানোর জন্য ত্রুটি সংশোধন করা হয়েছে (প্যাটার্ন 1 এর একটি এনক্যাপসুলেশন)
জর্জ ডেকেট

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

দয়া করে উত্তরটি দেখুন: স্ট্যাকওভারফ্লো.
com/

উত্তর:


196

সর্বোত্তম সমাধানটি সাধারণত RemoveAll()পদ্ধতিটি ব্যবহার করা হয় :

myList.RemoveAll(x => x.SomeProp == "SomeValue");

বা, যদি আপনার নির্দিষ্ট উপাদানগুলি সরানো প্রয়োজন :

MyListType[] elems = new[] { elem1, elem2 };
myList.RemoveAll(x => elems.Contains(x));

এটি ধরে নেওয়া হয় যে আপনার লুপটি অবশ্যই মুছে ফেলার উদ্দেশ্যে তৈরি করা হয়েছে। আপনি যদি না অতিরিক্ত প্রক্রিয়াকরণ করার প্রয়োজনীয়তা, অতঃপর যা উত্তম পদ্ধতি ব্যবহার করতে সাধারণত forবা whileলুপ, তারপর থেকে আপনি একটি গণনাকারী ব্যবহার করছেন না:

for (int i = myList.Count - 1; i >= 0; i--)
{
    // Do processing here, then...
    if (shouldRemoveCondition)
    {
        myList.RemoveAt(i);
    }
}

পিছনে যাওয়ার বিষয়টি নিশ্চিত করে যে আপনি কোনও উপাদান এড়িয়ে যাবেন না।

সম্পাদনার প্রতিক্রিয়া :

যদি আপনি আপাতদৃষ্টিতে স্বেচ্ছাসেবী উপাদানগুলি মুছে ফেলতে চলেছেন তবে সবচেয়ে সহজ পদ্ধতি হ'ল আপনি যে উপাদানগুলি সরাতে চান সেগুলি সন্ধান করতে হবে এবং তারপরে সেগুলি একবারে সরিয়ে ফেলতে হবে। এটার মতো কিছু:

List<int> toRemove = new List<int>();
foreach (var elem in myList)
{
    // Do some stuff

    // Check for removal
    if (needToRemoveAnElement)
    {
        toRemove.Add(elem);
    }
}

// Remove everything here
myList.RemoveAll(x => toRemove.Contains(x));

আপনার প্রতিক্রিয়া সম্পর্কে: পুরো লুপটি প্রক্রিয়া করার পরে না হয়ে আইটেমটি প্রক্রিয়াকরণের সময় আমাকে তাত্ক্ষণিকভাবে সরিয়ে ফেলতে হবে। আমি যে সমাধানটি ব্যবহার করছি তা হ'ল আমি যে কোনও আইটেম তাত্ক্ষণিকভাবে মুছতে চাই এবং সেগুলি পরে মুছতে চাই। এটি কোনও আদর্শ সমাধান নয় কারণ আমাকে পুরো জায়গা জুড়ে নুলের জন্য পরীক্ষা করতে হবে, তবে এটি কাজ করে।
জন স্টক

ভাবছেন যদি 'ইলেম' কোনও অন্তর্নির্মিত না হয়, তবে আমরা সম্পাদনা করা প্রতিক্রিয়া কোডটিতে যেভাবে এটি সরান এটি সমস্তভাবে ব্যবহার করতে পারি না।
ব্যবহারকারী এম

22

যদি আপনার উভয়কে অবশ্যই একটি অঙ্ক করা উচিত List<T>এবং এটি থেকে সরিয়ে ফেলা হয় তবে আমি কেবল একটি এর whileপরিবর্তে একটি লুপ ব্যবহার করার পরামর্শ দিইforeach

var index = 0;
while (index < myList.Count) {
  if (someCondition(myList[index])) {
    myList.RemoveAt(index);
  } else {
    index++;
  }
}

এটি আমার মতে গ্রহণযোগ্য উত্তর হওয়া উচিত। এটি আপনাকে আইটেমের একটি শর্টলিস্ট অপসারণ না করে পুনরায় পুনরাবৃত্তি না করে আপনার তালিকার বাকী আইটেমগুলি বিবেচনা করতে দেয়।
Slvrfn

13

আমি জানি এই পোস্টটি পুরানো, তবে আমি ভেবেছিলাম আমার জন্য যা কাজ করেছে তা আমি ভাগ করব।

গণনা করার জন্য তালিকার একটি অনুলিপি তৈরি করুন এবং তারপরে প্রতিটি লুপের জন্য আপনি অনুলিপি করা মানগুলিতে প্রক্রিয়া করতে পারেন এবং উত্স তালিকার সাথে যা কিছু মুছে ফেলতে / যুক্ত করতে পারেন / করতে পারেন।

private void ProcessAndRemove(IList<Item> list)
{
    foreach (var item in list.ToList())
    {
        if (item.DeterminingFactor > 10)
        {
            list.Remove(item);
        }
    }
}

".ToList ()" এ দুর্দান্ত ধারণা! সাধারণ হেড-স্লিপার, এবং আপনি স্টকটি "সরান ... ()" মেথ সরাসরি ব্যবহার করছেন না এমন ক্ষেত্রেও কাজ করে।
গ্যালাক্সিস

4
যদিও খুব অদক্ষ।
নওফাল

8

যখন আপনাকে কোনও তালিকার মাধ্যমে পুনরাবৃত্তি করতে হবে এবং লুপ চলাকালীন সময়ে এটি সংশোধন করতে পারে তখন লুপের জন্য একটি ব্যবহার করা ভাল:

for (int i = 0; i < myIntCollection.Count; i++)
{
    if (myIntCollection[i] == 42)
    {
        myIntCollection.Remove(i);
        i--;
    }
}

অবশ্যই আপনাকে অবশ্যই সাবধানতা অবলম্বন করতে হবে, উদাহরণস্বরূপ আমি হ্রাস পাচ্ছি iযখনই কোনও আইটেম সরানো হবে অন্যথায় আমরা এন্ট্রিগুলি এড়িয়ে যাব (বিকল্পটি তালিকার পিছনে পিছনে যেতে হবে)।

আপনার যদি লিনক থাকে তবে আপনার RemoveAllযেমন ঠিক তেমনি ব্যবহার করা উচিত lev


আপনি যখন বর্তমান উপাদানটি সরিয়ে দিচ্ছেন কেবল তখনই কাজ করে। আপনি যদি একটি স্বেচ্ছাসেবী উপাদান অপসারণ করছেন তবে আপনার সূচকটি বর্তমান সূচীতে / আগে বা পরে ছিল কিনা তা সিদ্ধান্ত নিতে হবে --i
কম্পুশিপ

আসল প্রশ্নটি এটিকে পরিষ্কার করে দেয়নি যে বর্তমানের তুলনায় অন্যান্য উপাদানগুলির মুছে ফেলা অবশ্যই সমর্থন করা উচিত, @ কমপুকশিপ। এটি পরিষ্কার হওয়ার পরে এই উত্তরটির কোনও পরিবর্তন হয়নি।
পেরেক

@ প্লেক, আমি বুঝতে পেরেছি, তাই আমার মন্তব্য।
কম্পুশিপ

5

আপনি তালিকাটি অঙ্কিত করার সাথে সাথে আপনি কেপিকে রাখতে চান তা একটি নতুন তালিকায় যুক্ত করুন। এরপরে, নতুন তালিকাটি নির্ধারণ করুনmyIntCollection

List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
List<int> newCollection=new List<int>(myIntCollection.Count);

foreach(int i in myIntCollection)
{
    if (i want to delete this)
        ///
    else
        newCollection.Add(i);
}
myIntCollection = newCollection;

2

আসুন আপনাকে কোড যুক্ত করুন:

List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);

আপনি পূর্বাঞ্চলে থাকাকালীন যদি তালিকাটি পরিবর্তন করতে চান তবে আপনাকে অবশ্যই টাইপ করতে হবে .ToList()

foreach(int i in myIntCollection.ToList())
{
    if (i == 42)
       myIntCollection.Remove(96);
    if (i == 25)
       myIntCollection.Remove(42);
}

1

তাদের জন্য এটি যেভাবে সহায়তা করতে পারে, আমি প্রেডিকেটের সাথে মেলে এমন আইটেমগুলি সরাতে এবং সরানো আইটেমগুলির তালিকাটি ফিরিয়ে দিতে এই এক্সটেনশন পদ্ধতিটি লিখেছিলাম।

    public static IList<T> RemoveAllKeepRemoved<T>(this IList<T> source, Predicate<T> predicate)
    {
        IList<T> removed = new List<T>();
        for (int i = source.Count - 1; i >= 0; i--)
        {
            T item = source[i];
            if (predicate(item))
            {
                removed.Add(item);
                source.RemoveAt(i);
            }
        }
        return removed;
    }

0

কেমন

int[] tmp = new int[myIntCollection.Count ()];
myIntCollection.CopyTo(tmp);
foreach(int i in tmp)
{
    myIntCollection.Remove(42); //The error is no longer here.
}

বর্তমান সি # তে, এটি যে foreach (int i in myIntCollection.ToArray()) { myIntCollection.Remove(42); }কোনও গণনাকারীর মতোই আবার লেখা যেতে পারে , এবং List<T>নেট .০.০ এ এমনকি এই পদ্ধতিটি বিশেষভাবে সমর্থন করে।
পলেক

0

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

private void RemoveItems()
{
    _newList.Clear();

    foreach (var item in _list)
    {
        item.Process();
        if (!item.NeedsRemoving())
            _newList.Add(item);
    }

    var swap = _list;
    _list = _newList;
    _newList = swap;
}

0

সন্ধান পেয়েছি আমি আমার সমাধানটি একই ধরণের সমস্যার সাথে ভাগ করব যেখানে আমি প্রক্রিয়া করার সময় তালিকা থেকে আইটেমগুলি সরিয়ে ফেলতে হবে।

সুতরাং মূলত "foreach" যা পুনরাবৃত্তি হওয়ার পরে তালিকা থেকে আইটেমটি সরিয়ে ফেলবে।

আমার পরীক্ষা:

var list = new List<TempLoopDto>();
list.Add(new TempLoopDto("Test1"));
list.Add(new TempLoopDto("Test2"));
list.Add(new TempLoopDto("Test3"));
list.Add(new TempLoopDto("Test4"));

list.PopForEach((item) =>
{
    Console.WriteLine($"Process {item.Name}");
});

Assert.That(list.Count, Is.EqualTo(0));

আমি এটি একটি এক্সটেনশন পদ্ধতি "পপফোরএচ" দিয়ে সমাধান করেছি যা কোনও ক্রিয়া সম্পাদন করবে এবং তারপরে আইটেমটি তালিকা থেকে সরিয়ে ফেলবে।

public static class ListExtensions
{
    public static void PopForEach<T>(this List<T> list, Action<T> action)
    {
        var index = 0;
        while (index < list.Count) {
            action(list[index]);
            list.RemoveAt(index);
        }
    }
}

আশা করি এটি যে কারও পক্ষে সহায়ক হতে পারে।

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