Nested Dictionary Change Value Problem

Hi Unity Community, thank you for looking at my post. I will get str8 to the point. I have having an issue with changing, the values in a nested Dictionary, There are no errors and most of it works as expected but it does not respect a particular key and changes the value for all entries.

I have set up a test that replicates my problem, if you run this class and then hit “A” you will see a debug output of the dictionary as I set it up (all as expected). Then you hit “B” and it will change the values of the dictionary in a very specific way. BUT ONLY for a specific day!, then you hit “C” and what you will find is that it changed the value for every day, ignoring the key for the day.

Here is the class. Sorry to include your name @aldonaletto but this seams right up your ally. Any idea?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TestScript : MonoBehaviour
{

	//The Master Dictionary to hold <location < day index < hour index < price>>>> 
	Dictionary <string,Dictionary<int,List<int>>> priceByHour;

	//The defualt price, value to change. 
	private int defaultPrice = 3;

	void Start ()
	{
		// Initilazing master Dictionary
		priceByHour = new Dictionary<string, Dictionary<int, List<int>>> ();

		// The locations
		List<string> locations = new List<string> {"NY"};//, "LA", "Chenarous", "Azaroth", "York", "Lancaster"};
		foreach (string loc in locations) {

			//Create Hours List with the prices
			List<int> hours = new List<int> ();
			for (int i = 0; i < 24; i ++) {
				hours.Add (defaultPrice);
			}

			// Create Days list with the hours and prices
			Dictionary<int,List<int>> days = new Dictionary<int, List<int>> ();
			for (int i = 0; i < 7; i ++) {
				days.Add (i, hours);
			}

			// add it all to the master dictionary.
			priceByHour.Add (loc, days);
		}
	}


	// To Test and review;
	void Update(){
		//Print out the content of the master dictionary
		if (Input.GetKeyDown (KeyCode.A)) {
			PrintOutMasterDictionary();
		}

		// change the values
		if (Input.GetKeyDown (KeyCode.B)) {
			ChangeValuesOfDayTwo();
		}

		// print againg to compare result
		if (Input.GetKey (KeyCode.C)) {
			PrintOutMasterDictionary();
		}
		// Notice that the dayToChange was set to index 4 but it changed the value of every over after the 6th hour to the new price! 
	}


	// Print out the result to compare after the change.
	void PrintOutMasterDictionary ()
	{
		foreach (KeyValuePair<string,Dictionary<int,List<int>>> masterEntry in priceByHour) {
			Debug.Log ("Location = " + masterEntry.Key);
			foreach (KeyValuePair<int,List<int>> day in masterEntry.Value) {
				Debug.Log ("Day = " + day.Key.ToString ());
				for (int i = 0; i < day.Value.Count; i ++) {
					Debug.Log ("Hour = " + i.ToString () + " has a value of " + day.Value *.ToString ());*
  •  		}*
    
  •  	}*
    
  •  }*
    
  • }*

  • void ChangeValuesOfDayTwo(){*

  •  // set change perameters*
    
  •  int dayToChange = 4;*
    
  •  int newPrice = 5;*
    
  •  string locationToChange = "NY";*
    
  •  int hourToChangeBehind = 6;*
    
  •  //Iterate through all possible hours*
    
  •  for (int i = 0; i < 24; i++) {*
    
  •  	// if the hour is greater than hour to change, change the value*
    
  •  	if(i > hourToChangeBehind){*
    

_ priceByHour[locationToChange][dayToChange] = newPrice;_
* }*
* }*
* }*
}

Thank you.

This behavior occurs because List is a reference type. Reference types are simply pointers to memory. When you pass them to a method only the reference is copied. It still points to the same object. This means the loop on line 31 is making a dictionary where all values are the exact same list. Not copies. You need to either copy the list or make a new one for each day.


Value Types and Reference Types

Value vs. Reference Types in C#

http://www.albahari.com/valuevsreftypes.aspx

Isn’t List a reference type?
Therefore isn’t the line

days.Add (i, hours);

relating the same list to every day instead of giving each day it’s own copy of the list?

Can’t try right now in this computer, but my first test from your script would be changing that line to something like

days.Add (i, new List<int>(hours));

just to be sure each day gets a separated copy

C# object types are reference types. This means that you need to ‘new up’ an hours list each time you add it to a days dictionary - otherwise a reference will be shared between each element of the days dictionary and any change to one element will apply to all elements.

Something similar to the following is probably what you are after.

foreach (string loc in locations)
{
    var days = new Dictionary<int, List<int>>();
    for (int i = 0; i < 7; i++)
    {
        var hours = new List<int>();
        for (int j = 0; j < 24; j++)
            hours.Add(defaultPrice);
        days.Add(i, hours);
    }
}