একটি অগ্রণী লুপে অভিধানের মানগুলি সম্পাদনা করা হচ্ছে


191

আমি অভিধান থেকে পাই চার্ট তৈরি করার চেষ্টা করছি। পাই চার্টটি প্রদর্শন করার আগে, আমি ডেটা পরিষ্কার করতে চাই। পাই এর 5% এরও কম হতে পারে এমন কোনও পাই স্লাইসগুলি সরিয়ে আমি "অন্যান্য" পাই স্লাইসে রাখছি। তবে আমি একটি পাচ্ছিCollection was modified; enumeration operation may not execute রানটাইমে ব্যতিক্রম ।

আমি বুঝতে পারছি কেন আপনি কোনও অভিধান থেকে আইটেমগুলি জুড়ে বা মুছতে পারবেন না কেন সেগুলি পুনরাবৃত্তি করার সময়। তবে আমি বুঝতে পারছি না কেন আপনি কেবল ফরচ লুপের মধ্যে বিদ্যমান কীটির জন্য কোনও মান পরিবর্তন করতে পারবেন না।

কোন পরামর্শ পুনরায়: আমার কোড ঠিক করা, প্রশংসা করা হবে।

Dictionary<string, int> colStates = new Dictionary<string,int>();
// ...
// Some code to populate colStates dictionary
// ...

int OtherCount = 0;

foreach(string key in colStates.Keys)
{

    double  Percent = colStates[key] / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

colStates.Add("Other", OtherCount);

উত্তর:


260

অভিধানে একটি মান নির্ধারণ করা তার অভ্যন্তরীণ "সংস্করণ নম্বর" আপডেট করে - যা পুনরুক্তিকারীকে এবং কী বা মান সংগ্রহের সাথে সম্পর্কিত কোনও পুনরাবৃত্তিকে অবৈধ করে দেয়।

আমি আপনার বক্তব্যটি দেখতে পাচ্ছি, তবে একই সাথে মানগুলি সংগ্রহের মধ্য-পুনরাবৃত্তির পরিবর্তন করতে পারে তবে এটি অদ্ভুত হবে - এবং সরলতার জন্য কেবলমাত্র একটি সংস্করণ নম্বর রয়েছে।

এই ধরণের জিনিস ঠিক করার স্বাভাবিক উপায় হ'ল হয় কীগুলির সংগ্রহটি আগেই অনুলিপি করা এবং অনুলিপিটি পুনরুক্ত করা, বা মূল সংগ্রহের উপর পুনরাবৃত্তি করা কিন্তু আপনি পুনরাবৃত্তি শেষ করার পরে প্রয়োগ করবেন এমন পরিবর্তনগুলির একটি সংগ্রহ বজায় রাখা।

উদাহরণ স্বরূপ:

প্রথমে চাবিগুলি অনুলিপি করা হচ্ছে

List<string> keys = new List<string>(colStates.Keys);
foreach(string key in keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

অথবা ...

পরিবর্তনগুলির একটি তালিকা তৈরি করা হচ্ছে

List<string> keysToNuke = new List<string>();
foreach(string key in colStates.Keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        keysToNuke.Add(key);
    }
}
foreach (string key in keysToNuke)
{
    colStates[key] = 0;
}

24
আমি জানি এটি পুরানো, তবে .NET 3.5 ব্যবহার করা হয় (বা এটি 4.0?) আপনি নিম্নরূপে লিনিকউ ব্যবহার করতে এবং অপব্যবহার করতে পারেন: ফোরচ (কলস্টেটস স্ট্রিং কী। টিসলিস্ট ()) {...}
ম্যাচটিন

6
@Machtyn: অবশ্যই - কিন্তু প্রশ্ন .NET 2.0 সম্পর্কে বিশেষভাবে ছিল, অন্যথায় আমি অবশ্যই হবে ব্যবহৃত LINQ আছে।
জন স্কিটি

"সংস্করণ নম্বর" অভিধানের দৃশ্যমান রাজ্যের অংশ বা বাস্তবায়নের বিশদ?
বেনামে কাউয়ার্ড

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

মাইক্রোসফ্ট ডক্স থেকে। নেট ফ্রেমওয়ার্ক ৪.৮ : পূর্ববর্তী বিবৃতিটি এনুমিরেটরের চারপাশে একটি মোড়ক যা এটি কেবলমাত্র সংগ্রহ থেকে পড়ার অনুমতি দেয়, এটি না লিখে। সুতরাং আমি বলব যে এটি একটি বাস্তবায়ন বিশদ যা ভবিষ্যতের সংস্করণে পরিবর্তিত হতে পারে। এবং যা দৃশ্যমান তা হ'ল গণকের ব্যবহারকারী এর চুক্তি লঙ্ঘন করেছে। তবে আমি ভুল হতে চাই ... ডিকশনারি সিরিয়াল করা হলে এটি সত্যই দৃশ্যমান।
বেনামে কাউয়ার্ড

81

ফোন করুন ToList()মধ্যে foreachলুপ। এইভাবে আমাদের একটি অস্থায়ী ভেরিয়েবল কপি দরকার নেই। এটি লিনকের উপর নির্ভর করে যা নেট .৩.৫ থেকে পাওয়া যায়।

using System.Linq;

foreach(string key in colStates.Keys.ToList())
{
  double  Percent = colStates[key] / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

খুব সুন্দর উন্নতি!
স্পিজিফিশ

1
এটা ব্যবহার করা ভাল হবে foreach(var pair in colStates.ToList())কী ব্যবহার করতে পারছে এড়াতে এবং মূল্য যা ডাকতে থাকার এড়াতে colStates[key]..
user2864740

21

আপনি এই লাইনে সংগ্রহটি পরিবর্তন করছেন:

কলস্টেটস [কী] = 0;

এটি করার মাধ্যমে, আপনি মূলত সেই মুহুর্তে কিছু মুছে ফেলা এবং পুনরায় প্রবেশ করিয়ে দিচ্ছেন (যতদূরই আইএনউমারেবল সম্পর্কিত।

আপনি যদি কোনও সদস্য সম্পাদনা করেন যে মানটি সংরক্ষণ করছেন তার করলে তা ঠিক হবে তবে আপনি নিজেই মানটি সম্পাদনা করছেন এবং আইনাম্বারেবল এটি পছন্দ করেন না।

আমি যে সমাধানটি ব্যবহার করেছি তা হ'ল ফোরচ লুপটি মুছে ফেলা এবং কেবল লুপের জন্য একটি ব্যবহার। লুপের জন্য একটি সহজ এমন পরিবর্তনগুলির জন্য যাচাই করে না যা আপনি জানেন যে সংগ্রহটি প্রভাবিত করবে না।

আপনি এটি কীভাবে করতে পারেন তা এখানে:

List<string> keys = new List<string>(colStates.Keys);
for(int i = 0; i < keys.Count; i++)
{
    string key = keys[i];
    double  Percent = colStates[key] / TotalCount;
    if (Percent < 0.05)    
    {        
        OtherCount += colStates[key];
        colStates[key] = 0;    
    }
}

আমি লুপ ব্যবহার করে এই সমস্যাটি পাই। অভিধান [সূচক] [কী] = "এবিসি", তবে এটি প্রাথমিক মান "xyz" এ ফিরে আসে
নিক চ্যান আবদুল্লাহ

1
এই কোডের সমাধানটি লুপের জন্য নয়: এটি কীগুলির তালিকাটি অনুলিপি করছে। (এটা এখনও যদি আপনি এটি একটি foreach লুপ রূপান্তরিত কাজ করবে।) ব্যবহার করে লুপ জন্য একটি ব্যবহার মানে হবে দ্বারা সমাধান colStates.Keysস্থানে keys
ইডব্রি

6

আপনি সরাসরি ফরইচ-তে কী বা মানগুলি সংশোধন করতে পারবেন না, তবে আপনি তাদের সদস্যদের সংশোধন করতে পারেন। উদাহরণস্বরূপ, এটি কাজ করা উচিত:

public class State {
    public int Value;
}

...

Dictionary<string, State> colStates = new Dictionary<string,State>();

int OtherCount = 0;
foreach(string key in colStates.Keys)
{
    double  Percent = colStates[key].Value / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key].Value;
        colStates[key].Value = 0;
    }
}

colStates.Add("Other", new State { Value =  OtherCount } );

3

কীভাবে আপনার অভিধানের বিরুদ্ধে কিছু লিনিক অনুসন্ধান করবেন এবং তার ফলাফলগুলিতে আপনার গ্রাফকে বাঁধবেন কীভাবে? ...

var under = colStates.Where(c => (decimal)c.Value / (decimal)totalCount < .05M);
var over = colStates.Where(c => (decimal)c.Value / (decimal)totalCount >= .05M);
var newColStates = over.Union(new Dictionary<string, int>() { { "Other", under.Sum(c => c.Value) } });

foreach (var item in newColStates)
{
    Console.WriteLine("{0}:{1}", item.Key, item.Value);
}

লিনক কি কেবল ২.৩ এ অবলম্বন নয়? আমি। নেট 2.0 ব্যবহার করছি।
অহোহো

আপনি এটি ২.০ থেকে সিস্টেম.কোর.ডিএলএল এর ৩.৫ সংস্করণের রেফারেন্স সহ ব্যবহার করতে পারেন - এটি যদি আপনি গ্রহণ করতে চান না তবে আমাকে জানান এবং আমি এই উত্তরটি মুছব।
স্কট আইভে

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

3

আপনি যদি সৃজনশীল বোধ করেন তবে আপনি এই জাতীয় কিছু করতে পারেন। আপনার পরিবর্তনগুলি করতে অভিধানের মাধ্যমে পিছনে লুপ করুন।

Dictionary<string, int> collection = new Dictionary<string, int>();
collection.Add("value1", 9);
collection.Add("value2", 7);
collection.Add("value3", 5);
collection.Add("value4", 3);
collection.Add("value5", 1);

for (int i = collection.Keys.Count; i-- > 0; ) {
    if (collection.Values.ElementAt(i) < 5) {
        collection.Remove(collection.Keys.ElementAt(i)); ;
    }

}

অবশ্যই অভিন্ন নয়, তবে আপনি যে কোনও উপায়ে আগ্রহী হতে পারেন ...


2

আপনার জায়গায় পরিবর্তনের পরিবর্তে পুরানো থেকে একটি নতুন অভিধান তৈরি করা দরকার। কিছু কী (কীভ্যালিউপায়ার <>> এর উপরে কী কী লুকিং ব্যবহার না করে পুনরাবৃত্তি করতে হবে):

int otherCount = 0;
int totalCounts = colStates.Values.Sum();
var newDict = new Dictionary<string,int>();
foreach (var kv in colStates) {
  if (kv.Value/(double)totalCounts < 0.05) {
    otherCount += kv.Value;
  } else {
    newDict.Add(kv.Key, kv.Value);
  }
}
if (otherCount > 0) {
  newDict.Add("Other", otherCount);
}

colStates = newDict;

1

.NET 4.5 দিয়ে শুরু করে আপনি সমকালীন অভিধানের সাহায্যে এটি করতে পারেন :

using System.Collections.Concurrent;

var colStates = new ConcurrentDictionary<string,int>();
colStates["foo"] = 1;
colStates["bar"] = 2;
colStates["baz"] = 3;

int OtherCount = 0;
int TotalCount = 100;

foreach(string key in colStates.Keys)
{
    double Percent = (double)colStates[key] / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

colStates.TryAdd("Other", OtherCount);

তবে খেয়াল করুন যে এর কার্য সম্পাদন আসলে আরও খারাপ যে সরল foreach dictionary.Kes.ToArray():

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class ConcurrentVsRegularDictionary
{
    private readonly Random _rand;
    private const int Count = 1_000;

    public ConcurrentVsRegularDictionary()
    {
        _rand = new Random();
    }

    [Benchmark]
    public void ConcurrentDictionary()
    {
        var dict = new ConcurrentDictionary<int, int>();
        Populate(dict);

        foreach (var key in dict.Keys)
        {
            dict[key] = _rand.Next();
        }
    }

    [Benchmark]
    public void Dictionary()
    {
        var dict = new Dictionary<int, int>();
        Populate(dict);

        foreach (var key in dict.Keys.ToArray())
        {
            dict[key] = _rand.Next();
        }
    }

    private void Populate(IDictionary<int, int> dictionary)
    {
        for (int i = 0; i < Count; i++)
        {
            dictionary[i] = 0;
        }
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        BenchmarkRunner.Run<ConcurrentVsRegularDictionary>();
    }
}

ফলাফল:

              Method |      Mean |     Error |    StdDev |
--------------------- |----------:|----------:|----------:|
 ConcurrentDictionary | 182.24 us | 3.1507 us | 2.7930 us |
           Dictionary |  47.01 us | 0.4824 us | 0.4512 us |

1

আপনি সংগ্রহটি পরিবর্তন করতে পারবেন না, মানগুলিও নয়। আপনি এই কেসগুলি সংরক্ষণ করতে এবং পরে এগুলি সরাতে পারেন। এটি এই মত শেষ হবে:

Dictionary<string, int> colStates = new Dictionary<string, int>();
// ...
// Some code to populate colStates dictionary
// ...

int OtherCount = 0;
List<string> notRelevantKeys = new List<string>();

foreach (string key in colStates.Keys)
{

    double Percent = colStates[key] / colStates.Count;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key];
        notRelevantKeys.Add(key);
    }
}

foreach (string key in notRelevantKeys)
{
    colStates[key] = 0;
}

colStates.Add("Other", OtherCount);

আপনি সংগ্রহটি পরিবর্তন করতে পারেন । আপনি কোনও সংশোধিত সংগ্রহে একটি পুনরুক্তি ব্যবহার করে রাখতে পারবেন না
ব্যবহারকারী 2864740

0

দাবি অস্বীকার: আমি খুব বেশি সি # করি না

আপনি হ্যাশ টেবিলে সঞ্চিত ডিকশনারি ইন্ট্রি অবজেক্টটি সংশোধন করার চেষ্টা করছেন। হ্যাশটেবল কেবলমাত্র একটি জিনিস সঞ্চয় করে - আপনার অভিধানএন্ট্রির উদাহরণ। কী বা মানটি পরিবর্তন করা হ্যাশটবেল পরিবর্তন করতে এবং শনাক্তকারীকে অবৈধ করে তুলতে যথেষ্ট।

আপনি এটি লুপের বাইরেও করতে পারেন:

if(hashtable.Contains(key))
{
    hashtable[key] = value;
}

প্রথমে আপনি যে মানগুলি পরিবর্তন করতে চান তার সমস্ত কীগুলির একটি তালিকা তৈরি করে তার পরিবর্তে সেই তালিকার মাধ্যমে পুনরাবৃত্তি করতে পারেন।


0

আপনি এর একটি তালিকা অনুলিপি তৈরি dict.Valuesকরতে পারেন, তারপরে আপনি List.ForEachপুনরাবৃত্তির জন্য ল্যাম্বদা ফাংশনটি ব্যবহার করতে পারেন (বা একটি foreachলুপ, যেমনটি আগে প্রস্তাবিত হয়েছিল)।

new List<string>(myDict.Values).ForEach(str =>
{
  //Use str in any other way you need here.
  Console.WriteLine(str);
});

0

অন্যান্য উত্তরের সঙ্গে বরাবর, আমি নোট চাই যে আপনি যদি পেতে sortedDictionary.Keysবা sortedDictionary.Valuesসঙ্গে তাদের উপর লুপ এবং তারপর foreach, এছাড়াও আপনি মাধ্যমে সাজানো ক্রম যান। এর কারণ এই যে পদ্ধতিগুলি ফিরে আসে System.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollectionবা SortedDictionary<TKey,TValue>.ValueCollectionবস্তুগুলি, যা মূল অভিধানের বাছাই করে।


0

এই উত্তরটি দুটি সমাধানের তুলনার জন্য, প্রস্তাবিত সমাধান নয়।

অন্যান্য উত্তরগুলি প্রস্তাবিত হিসাবে অন্য তালিকা তৈরি করার পরিবর্তে, আপনি লুপ স্টপ অবস্থার জন্য এবং কীটি পেতে forঅভিধান ব্যবহার করে একটি লুপ ব্যবহার করতে পারেন ।CountKeys.ElementAt(i)

for (int i = 0; i < dictionary.Count; i++)
{
    dictionary[dictionary.Keys.ElementAt(i)] = 0;
}

প্রথমত আমি ভেবেছিলাম এটি আরও কার্যকর হবে কারণ আমাদের কী তালিকা তৈরি করার দরকার নেই। একটি পরীক্ষা চালানোর পরে আমি দেখতে পেলাম যে forলুপের সমাধানটি অনেক কম দক্ষ। কারণ হ'ল সম্পত্তিতে ElementAtও (এন) dictionary.Keysহওয়ায় এটি সংগ্রহের শুরু থেকে এটি নবম আইটেমে না যাওয়া পর্যন্ত অনুসন্ধান করে।

টেস্ট:

int iterations = 10;
int dictionarySize = 10000;
Stopwatch sw = new Stopwatch();

Console.WriteLine("Creating dictionary...");
Dictionary<string, int> dictionary = new Dictionary<string, int>(dictionarySize);
for (int i = 0; i < dictionarySize; i++)
{
    dictionary.Add(i.ToString(), i);
}
Console.WriteLine("Done");

Console.WriteLine("Starting tests...");

// for loop test
sw.Restart();
for (int i = 0; i < iterations; i++)
{
    for (int j = 0; j < dictionary.Count; j++)
    {
        dictionary[dictionary.Keys.ElementAt(j)] = 3;
    }
}
sw.Stop();
Console.WriteLine($"for loop Test:     {sw.ElapsedMilliseconds} ms");

// foreach loop test
sw.Restart();
for (int i = 0; i < iterations; i++)
{
    foreach (string key in dictionary.Keys.ToList())
    {
        dictionary[key] = 3;
    }
}
sw.Stop();
Console.WriteLine($"foreach loop Test: {sw.ElapsedMilliseconds} ms");

Console.WriteLine("Done");

ফলাফল:

Creating dictionary...
Done
Starting tests...
for loop Test:     2367 ms
foreach loop Test: 3 ms
Done
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.