Link to home
Start Free TrialLog in
Avatar of jxbma
jxbmaFlag for United States of America

asked on

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

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
Avatar of kaufmed
kaufmed
Flag of United States of America image

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

Avatar of jxbma

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of kaufmed
kaufmed
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

Avatar of jxbma

ASKER

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