We help IT Professionals succeed at work.

How do I iterate over a List<KeyValuePair<>> and update the value of my KeyValuePair<>?

jxbma
jxbma asked
on
I have a List<T> defined below.
===========================================================
List<KeyValuePair<MyClass, decimal>> tciList = new List<KeyValuePair<MyClass, decimal>>();
int myValue = 12;

This is an in memory list, and I'd like to update values based on a selection criteria.

Currently, I can write a LINQ query that returns all the values based on that criteria.


TAKE #1:
-----------
foreach (var item in from v in tciList .AsEnumerable()
                               where (v.Key as MyClass).DiscountID == (int)myValue
                               select v)
{
         item.Value = 100;
}


TAKE #2:
-----------
foreach (KeyValuePair<MyClass, decimal>item in tciList.AsEnumerable())
{
         item.Value = 100;
}



In either case, I get the following error:
===========================================================================

Error      404      
Property or  Indexer  
'System.Collections.Generic.KeyValuePair<Explorica.Sales.Business.TravelerTourCenterInformation,decimal>.Value'
cannot be assigned to -- it is read only
===========================================================================


What's going on here? Is there a copy of the list get returned for the iteration?
I googled and saw reference to some changes between Beta 2 and Beta 3 which after which folks started
encountering the error.

How do I iterate over the list and make changes to values in my KeyValuePair<>?

Thanks,
JohnB
Comment
Watch Question

Most Valuable Expert 2011
Top Expert 2015

Commented:
You can't modify the iterator variable in a foreach. You should, however, be able to index into the original collection using the key available in the foreach:
foreach (KeyValuePair<MyClass, decimal>item in tciList.AsEnumerable())
{
         tciList[item].Value = 100;
}

Open in new window

Try something like this:
var tmpList = new List<KeyValuePair<Test, decimal>>();

tmpList.Add(new KeyValuePair<Test,decimal>(new Test(){ Val1="a" }, 100));
tmpList.Add(new KeyValuePair<Test, decimal>(new Test() { Val1 = "b" }, 200));
tmpList.Add(new KeyValuePair<Test, decimal>(new Test() { Val1 = "c" }, 300));
tmpList.Add(new KeyValuePair<Test, decimal>(new Test() { Val1 = "d" }, 400));
tmpList.Add(new KeyValuePair<Test, decimal>(new Test() { Val1 = "e" }, 600));

for (int i = 0; i < tmpList.Count; i++)
{
    var newKvp = new KeyValuePair<Test, decimal>(tmpList[i].Key, 100);
    tmpList.RemoveAt(i);
    tmpList.Insert(i, newKvp);
}

Open in new window

jxbmaSoftware Consultant

Author

Commented:
Hi kaufmed:


When I attempted your suggestion, I get this additional error:
------------------------------------------------------------------------------------
Error      457      
The best overloaded method match for
'System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<MyClass,decimal>>.this[int]' has some invalid arguments      


So, I swapped out the and tried iterating via an index.

At that pointm, it still gives the same error that I encountered.

Do I actually have to remove/add a new list item whenever I want to update my values in a List<KeyValuePair<>>?
That seems a little long winded/obscure to have to do that.
There must be a straight forward to do this...?

Thanks,
JohnB
Most Valuable Expert 2011
Top Expert 2015
Commented:
My apologies, I forgot that List<> only indexes based on index.

Yes, KeyValuePairs appear to be immutable--which means you will have to add a new one whenever the value changes.

An example more fitting to your situation would be:
for (int i = 0; i < tciList.Count; i++)
{
    if (tciList[i].Key.DiscountID == myValue)
    {
        tciList[i] = new KeyValuePair<MyClass, decimal>(tciList[i].Key, 100);
    }
}

Open in new window

Fernando SotoRetired
Distinguished Expert 2017

Commented:
Hi jxbma;

You could create your own class to represent the Key, Value Pair, See code snippet for an example.

Fernando
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    List<KVP> tciKVPList = new List<KVP>();

    private void Form1_Load(object sender, EventArgs e)
    {
        tciKVPList.Add(new KVP() { Key = new MyClass() { myClassValue = 1, DiscountID = 2 }, Value = 1 });
        tciKVPList.Add(new KVP() { Key = new MyClass() { myClassValue = 2, DiscountID = 10 }, Value = 2 });
        tciKVPList.Add(new KVP() { Key = new MyClass() { myClassValue = 3, DiscountID = 3 }, Value = 10 });
        tciKVPList.Add(new KVP() { Key = new MyClass() { myClassValue = 4, DiscountID = 10 }, Value = 10 });
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //================================================================
        // From The last question
        int myDiscountID = 10;

        int count = (from v in tciKVPList.AsEnumerable()
                     where (v.Key as MyClass).DiscountID == myDiscountID
                     select v).Count();

        Console.WriteLine("Count = " + count);
        //================================================================
        // Your TAKE #1:
        int myValue = 3;
        foreach (KVP item in from v in tciKVPList.AsEnumerable()
                             where (v as KVP).Key.DiscountID == (int)myValue
                             select v)
        {
            item.Value = 100;
        }

        Console.WriteLine("All values that meet the test have been modified to value 100");
        //================================================================
        // Your TAKE #2:
        foreach (KVP item in tciKVPList.AsEnumerable())
        {
            item.Value = 100;
        }
        Console.WriteLine("All values have been modified to value 100");
    }
}

public class MyClass
{
    public int myClassValue { get; set; }
    public int DiscountID { get; set; }
}

// This class represents the Key, Value Pair.
public class KVP
{
    public MyClass Key { get; set; }
    public decimal Value { get; set; }
}

Open in new window

jxbmaSoftware Consultant

Author

Commented:
Right on track. I've used it several times.