C# dictionary value is updating automatically - c#

In my recent project im trying to make a dictionary with key as a string and value as List of string(List) and adding value in dictionary using for loop ,
but the problem is that after first iteration when I Update the List for second iteration it is automatically changing in the first key value pair.
for example in first iteration it is saving key as apple and value as list {cucumber,chilli,tomato,apple} its fine but after first iteration when i update list to {cucumber,chilli,tomato,apple,mango} and saving it to second key mango it is also updating the first value to {cucumber,chilli,tomato,apple,mango}.
var mylist = new List<string>()
{
"cucumber",
"chilli",
"tomato"
};
var yourlist = new List<string>()
{
"apple",
"mango",
"banana"
};
var dict = new Dictionary<string, List<string>>();
foreach (var i in yourlist)
{
mylist.Add(i);
dict.Add(i,mylist);
}
foreach(var d in dict.Keys)
{
foreach(var l in dict[d])
{
Console.WriteLine(l);
}
}

The dictionary entries' Value properties are always the same list, so anything you do to one, ends up showing in all of them (because there is only one)
Take a look at the code below; if you understand why a and b here both show the same change, then you should understand that your dictionary scenario is essentially the same
var list = new List<string>(){ "hello", "world" };
var a = list;
var b = list;
a.Add("there");
Console.Write(b.Count); //shows 3
If you don't understand why a, b and list above all refer to the same list, then drop a comment and I'll add some more explanation
As to what you should do about your "issue", it's not entirely clear to me what you're hoping to do but if you made sure that each key in the dictionary associated with a new list rather than the same one, then changes to one key's list would not show in other keys' lists:
dict.Add(i, new List<string>(mylist));
This makes a new list per key, and initializes the new list with the items present in mylist at the time (my list grows each pass of the loop)

Related

Odd behavior with the List/Dictionary .Clear() method [duplicate]

This question already has answers here:
Why does adding a new value to list<> overwrite previous values in the list<>
(2 answers)
Closed 4 years ago.
I have a code block similar to this:
List<Object> returnObjDataLine = new List<object>();
foreach (var tuple in dataPerTime)
{
returnObjDataLine.Clear();
returnObjDataLine.Add(tuple.Item1);
foreach (var line in PlotLines)
{
if (tuple.Item2.Equals(line.Key))
{
returnObjDataLine.Add(tuple.Item3);
}
else
{
returnObjDataLine.Add(null);
}
}
returnObjData.Add(returnObjDataLine);
}
However the Clear() method clears out the data already added to the returnObjData Dictionary, sort of. If there are 10,000 tuples, then after the loop runs the returnObjData will contain 10,000 instances of the very last data piece added (Clear() is not called after the last iteration).
If I modify the code to create a new List each iteration:
foreach (var tuple in dataPerTime)
{
List<Object> returnObjDataLine = new List<object>();
returnObjDataLine.Add(tuple.Item1);
foreach (var line in PlotLines)
{
if (tuple.Item2.Equals(line.Key))
{
returnObjDataLine.Add(tuple.Item3);
}
else
{
returnObjDataLine.Add(null);
}
}
returnObjData.Add(returnObjDataLine);
}
the loading loop works correctly but this seems very expensive as there can be 10s if not 100s of thousands of iterations required. Creating a new object every time seems to be inefficient.
What am I missing with Clear()? Is there some sort of "commit" that needs to be called first?
Looks like what you need to do is have a temporary list and a long term list... Something like this:
List<Object> longTermObjects = new List<object>();
foreach (var tuple in dataPerTime)
{
List<Object> returnObjDataLine = new List<Object>();
returnObjDataLine.Add(tuple.Item1);
foreach (var line in PlotLines)
{
if (tuple.Item2.Equals(line.Key))
{
returnObjDataLine.Add(tuple.Item3);
}
else
{
returnObjDataLine.Add(null);
}
}
longTermObjects.Add(returnObjDataLine);
}
This will give you a clean returnObjDataLine each iteration without removing the referenced items in longTermObjects.
Edit To Add Reference Type Information:
By default .NET will store 1 copy of an object into memory, and then "reference" that object anywhere you use it. Take the following example:
int A = 1;
int B = A;
Console.WriteLine($"A = {A}");
Console.WriteLine($"B = {B}");
A = 0;
Console.WriteLine($"A = {A}");
Console.WriteLine($"B = {B}");
Result:
A = 1
B = 1
A = 0
B = 0
Why you ask does B = 0 on the 4th line? Because B had a REFERENCE to A, it didn't contain the actual VALUE of A, so when A changed, B changed as well.
If I wanted B to contain only the value of A then I would need a way to create a new "value" instead of a reference. The way you do this varies for each type of object. One way and probably not the best would be something like:
int B = int.Parse(A.ToString());
Which would convert A to a string representing the value of A and then into a new int with the Parse. Now I have the value stored in B instead of just a reference.
If I wanted to do the same thing with a table of objects then I would have to do something like this:
List<MyObject> oldList = new List<MyObject>();
//Put some objects into oldList
List<MyObject> newList = oldList.Select(x => new MyObject(x.Param1, x.Param2)).ToList();
In this example I am basically taking each object in oldList and creating a new MyObject which is then put into newList.

Add Items To a existing List In a dictionary

I have a dictionary as below
var dicAclWithCommonDsEffectivity = new Dictionary<string, List<int>>();
I have a list as below
var dsList=new List<int>();
For each item in dsList I will search in dicAclWithCommonDsEffectivity dictionary for the matching values in the list. If i find a match I take its Keys and form a new Key combining all the keys. I will create a new list and add item.
foreach (int i in dsList)
{
var aclWithmatchingDS = dicAclWithCommonDsEffectivity.Where(x => x.Value.Contains(i)).Select(x=>x.Key);
if (aclWithmatchingDS.Count() > 0)
{
string NewKey= aclWithmatchingDS.key1+","aclWithmatchingDS.key2 ;
//if NewKey is not there in dictionary
var lst=new List<int>(){i};
//Add item to dictionary
//else if item is present append item to list
//oldkey,{oldlistItem,i};
}
}
For the next item in dsList if there is a matching key then I have to add the item to the list inside new dictionary.
How to add new item to the list in a dictionary without creating new list.
You probably want something like that:
if (dicAclWithCommonDsEffectivity.ContainsKey(NewKey))
{
dicAclWithCommonDsEffectivity[NewKey].Add(i)
}
else
{
dicAclWithCommonDsEffectivity.Add(NewKey, lst); // or simply do new List<int>(){ i } instead of creating lst earlier
}
I suggest TryGetValue method which is typical in such cases:
List<int> list;
if (dicAclWithCommonDsEffectivity.TryGetValue(NewKey, out list))
list.Add(i);
else
dicAclWithCommonDsEffectivity.Add(NewKey, new List<int>() {i});
In case of C# 7.0 you can get rid of list declaration:
if (dicAclWithCommonDsEffectivity.TryGetValue(NewKey, out var list))
list.Add(i);
else
dicAclWithCommonDsEffectivity.Add(NewKey, new List<int>() {i});
Get first KeyValue pair in dicAclWithCommonDsEffectivityand add it to the list, which is the value here and it can be accessed directly:
if (aclWithmatchingDS.Count() > 0)
{
dicAclWithCommonDsEffectivity.Add(NewKey,lst);
}
else
{
aclWithmatchingDS.First().Value.Add("Here add your item");
}
Let me clarify before suggestion, So You want to check for existance of a key in the dictionary, according to some condition, If specific key is present means you want to add the new item to corresponding key or else you want to create a new key and a new list with the new item, if I understand the requirement correctly means you can try the following:
if(dicAclWithCommonDsEffectivity.ConainsKey(NewKey))
{
aclWithmatchingDS[NewKey].Add(i);
}
else
{
aclWithmatchingDS.Add(NewKey, new List<int>(){i});
}

Why am I getting ArgumentOutOfRangeException in Lists?

I am using List of Lists in my project. When i run program i get ArgumentOutOfRangeException. But there is no range specified in list.
I declared list like this:
public static List<List<string>> list = new List<List<string>>();
Now i want to add my name in the first "list" which is in the List of lists.
list[0].Add("Hussam"); //Here i get ArgumentOutOfRange Exception.
What should I do now?
But there is no range specified in list
No, there's an index specified (as an argument), and that's what's out of range. Look at your code:
list[0].Add("Hussam");
That's trying to use the first list in list - but is list is empty, there is no first element. The range of valid arguments to the indexer is empty, basically.
So first you want:
list.Add(new List<string>());
Now list[0] will correctly refer to your empty List<string>, so you can add "Hussam" to it.
You want to add an item to the first item in an empty list... That isn't going to work. First, add the list inside the other list:
public static List<List<string>> list = new List<List<string>>();
List<string> innerList = new List<string>();
list.Add(innerList);
innerList.Add("Hussam");
Why are you creating a list of a list? Wouldn't List suffice? What is happening here is the inner list is not being initialized.
list.Add(new List<string>());
list[0].Add("Jimmy");
In this case ocurred an exception because you tried acess an index which not exists, then you must add an inner initial list, which could be done follows:
list.Add(new new List<string>());
Or, if you want add an first name directly:
list.Add(new new List<string>(){"Hussam"});
Ok so first, you have to understand that the "index" only comes after the value has been declared. Lists behave different. They are not like arrays. You get the index in which you want to store the item and when you do that, you use the code array[index] = value;.
But in a List, to give a value to a completely new item, you use the method Add(value).
So here's a reminder: Systems.Collections.Generic.List<> has nothing to do with array[ ]s
You cannot access list[0] as there is no item at index 0. The list is empty.
You need to add a new List like this:
list.Add(new List<string> { "Hussam" });
or, assign a list to index 0 and then add to it as per your posted code:
list.Add(new List<string>());
list[0].Add("Hussam");
If you don't always know if the list will be be empty or not you can use FirstOrDefault (a LINQ method) to check if there is any entry at index 0 and assign one if not, otherwise use the existing inner list:
var innerList = list.FirstOrDefault();
if (innerList == null)
{
innerList = new List<string>();
list.Add(innerList);
}
innerList.Add("Hussam");
The problem is, your nested list hasn't been initialized, with anything.
So, calling the first item of the nested list is correctly telling you there is nothing in it.
To verify:
int superlistCounter = 1;
int sublistCounter = 1;
foreach(var sublist in list)
{
Console.WriteLine("Now in List #" + superlistCounter);
foreach(var item in sublist)
{
Console.WriteLine("List item #" + sublistCounter + ": " + item)
}
}
The output will be:
Now in List #1
It sounds like you're expecting:
Now in List #1
List Item #1: Hussam
To fix this, simply initialize your list!
public static List<List<string>> list = new List<List<string>>();
// ...
List<string> subList1 = new List<string>();
list.Add(subList1);
subList1.Add("Hussam");

Iterating over a growing dictionary

I am working with C# and I have a dictionary called intervalRecordsPerObject of type Dictionary<string, List<TimeInterval>>. I need to iterate through the dictionary. The problem is: everytime I iterate through the dictionary, more KeyValuePairs may get added to it. As the dictionary grows, I need to keep iterating over the new entries too.
Firstly, I did this: A simple foreach loop that gave me an InvalidOperationException saying
Collection was modified; enumeration operation may not execute.
I know I cannot iterate over the Dictionary this way if it keeps changing as C# converts it with ToList() before foreach loop.
I know I can copy the keys to a temporary array, iterate over the dictionary using simple for loop and Count and whenever a new entry is added to the dictionary, add the corresponding key to the array too. Now, the problem is a simple array cannot grow dynamically and I don't know beforehand what the required size could be.
To move ahead, I thought I'd do this:
List<string> keyList = new List<string>(intervalRecordsPerObject.Count);
intervalRecordsPerObject.Keys.CopyTo(keyList.ToArray(), 0);
I cannot do this either. keyList is currently empty and therefore keyList.toArray() returns an array of length 0 which gives me an ArgumentException saying
Destination array is not long enough to copy all the items in the
collection. Check array index and length.
I am stuck! Any idea what more can I try? Thanks for any help.
Addition 1:
The dictionary stores the time intervals for which a particular object is present. Key is the ID of the object. New entries may get added in every iteration (worst case) or may not get added even once. Whether or not entries are added is decided by a few conditions (whether the object overlaps with some other intervals, etc.). This triggers a change in the ID and the corresponding interval list which is then added as a new entry to the dictionary.
Something like this:
List<string> keys = dict.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
var key = keys[i];
List<TimeInterval> value;
if (!dict.TryGetValue(key, out value))
{
continue;
}
dict.Add("NewKey", yourValue);
keys.Add("NewKey");
}
The trick here is that you enumerate the List<T> by index! In this way, even if you add new elements, the for (...) will "catch" them.
Other possible solution, by using a temporary Dictionary<,>:
// The main dictionary
var dict = new Dictionary<string, List<TimeInterval>>();
// The temporary dictionary where new keys are added
var next = new Dictionary<string, List<TimeInterval>>();
// current will contain dict or the various instances of next
// (multiple new Dictionary<string, List<TimeInterval>>(); can
// be created)
var current = dict;
while (true)
{
foreach (var kv in current)
{
// if necessary
List<TimeInterval> value = null;
// We add items only to next, that will be processed
// in the next while (true) cycle
next.Add("NewKey", value);
}
if (next.Count == 0)
{
// Nothing was added in this cycle, we have finished
break;
}
foreach (var kv in next)
{
dict.Add(kv.Key, kv.Value);
}
current = next;
next = new Dictionary<string, List<TimeInterval>>();
}
You can access the Keys by positions rather than by content and use a normal For loop (allowing additions/removals without any restriction).
for (int i = 0; i < dict.Keys.Count; i++)
{
string curKey = dict.Keys.ElementAt(i);
TimeInterval curVal = dict.Values.ElementAt(i);
//TimeInterval curVal = dict[curKey];
//Can add or remove entries
}

Grouping Items In A List View

I have a Dictionary that is declared thusly:
Dictionary myDictionary<string, List<FCPort>> = new Dictionary<string, List<FCPort>>();
the key is a string representing a switch name. The value is a list of port objects for that switch. I am trying to add the items in the Dictionary to a ListView with this code:
foreach (KeyValuePair<string, List<FCPort>> portpair in results)
{
ListViewItem item1 = new ListViewItem(portpair.Key);
foreach (FCPort port in portpair.Value)
{
item1.SubItems.Add(port.FCIDList[0]);
item1.SubItems.Add(port.WWPNList[0]);
item1.SubItems.Add(port.TextSerializePortName());
this.ResultsListView.Items.Add(item1);
}
}
However, I get a run-time error basically saying that I have a duplicate item in the list. That makes sense. I am attempting to group by the dictinoary key (the switch name). Is there a way to somehow group the items in the listview, or dynamically add Listviews to the GroupBox on the fly? Basically add a new ListView for each key in the Dictionary? I am still learning C# and forms are still new.
you could use LINQ lookup to group by your key selector.
and extend your portpair to enumerable when add to into listview subitems
This is the code snippet I did sometimes hopefully could help you.
Dictionary<String, Country> dict = new Dictionary<string, Country>();
dict.Add("Toronto", Country.Canada);
dict.Add("New York", Country.US);
dict.Add("Vancover", Country.Canada);
dict.Add("Seattle", Country.US);
dict.Add("Fredericton", Country.Canada);
Lookup<Country,String> lookup = (Lookup<Country,String>) dict.ToLookup(pair =>pair.Value, pair => pair.Key);
foreach (var countryGroup in lookup)
{
item = new ListViewItem(countryGroup.Key.ToString());
item.SubItems.Add(string.Format("{0}", string.Join(",", countryGroup.Select(s => "#" + s))));
lv.Items.Add(item);
item = null;
}

Categories