I have a list of objects:
List<NPortfolio> Portfolios = new List<NPortfolio>();
Portfolios.Add(new NPortfolio(1, "1", emptyPositions));
Portfolios.Add(new NPortfolio(2, "2", emptyPositions));
Now i want to call a Method on the object that modifies its properties:
Portfolios[0].UpdatePositions(db.GetPortfolio(1, Today));
The method is this:
public void UpdatePositions(Dictionary<string, double> valuepairs)
{
foreach (var k in this.positions.Keys.ToList())
{
if (valuepairs.ContainsKey(k))
this.positions[k] = valuepairs[k];
}
}
This works, but the problem is that when I try to update just the first item of the list:
Portfolios[0].UpdatePositions(db.GetPortfolio(1, Today));
ALL ITEMS OF THE LIST ARE UPDATED!!!
I cannot find why all items are updated and not only item 0.
Please help this is really an headache
many thanks
class definition:
public class NPortfolio
{
public string p_id { get; set; }
public int p_nr { get; set; }
private Dictionary<string, double> positions;
public NPortfolio(int nr, string id, Dictionary<string, double> pos)
{
p_nr = nr;
p_id = id;
positions = pos;
}
public void UpdatePositions(Dictionary<string, double> valuepairs)
{
foreach (var k in this.positions.Keys.ToList())
{
if (valuepairs.ContainsKey(k))
this.positions[k] = valuepairs[k];
}
}
public Dictionary<string, double> getPositions()
{
return positions;
}
}
The problem is from this
Portfolios.Add(new NPortfolio(1, "1", emptyPositions));
Portfolios.Add(new NPortfolio(2, "2", emptyPositions));
You are passing the same dictionary to both classes, so if you modify one of the classes you modify both instances.
You must create a new dictionary inside the constructor of NPortfolio so each class has a unique copy.
public NPortfolio(int nr, string id, Dictionary<string, double> pos)
{
p_nr = nr;
p_id = id;
positions = new Dictionary<string, double>(pos);
}
This will make a shallow copy of the dictionary and should solve your issue for now.
You're passing the same dictionary into your objects. So when you update it in one you end up seeing the changes in the other. You should create a new dictionary inside your constructor and populate it with the values passed in.
public NPortfolio(int nr, string id, Dictionary<string, double> pos)
{
p_nr = nr;
p_id = id;
positions = new Dictionary<string, double>(pos);
}
Related
i have a problem with updating keys and values in dictionary, dictionary loses data everytime when new StreamReader runs on. I wanted StreamReader to read list and when it reads second path keys and values in dictionary are already lost. I would be greateful for help.
class TT_connect
{
public Dictionary<string, double> s_c = new Dictionary<string, double>();
public TT_connect(List<string> tempAS){
Dictionary<string, double> s_a = new Dictionary<string, double>();
foreach(string a in tempAS)
{
using (var s = new StreamReader(a))
{
while (!s.EndOfStream)
{
var l_a = s.ReadLine();
var l_b = l_a.Split(';');
if (s_a.Keys.Contains(l_b[0]))
{
double.TryParse(l_b[1], out double l1);
s_a[l_b[0]] += l1;
}
if (!s_a.Keys.Contains(l_b[0]))
{
double.TryParse(l_b[1], out double l2);
s_a.Add(l_b[0], l2);
}
}
}
}
foreach(KeyValuePair<string,double> s in s_a)
{
s_c.Add(s.Key, s.Value);
}
}
}
Based on Roberts comments i have tried to clean up the implementation a bit.
class TT_connect
{
private string _fileLocation;
public Dictionary<string, double> dict;
public TT_connect(string fileLocation)
{
dict = new Dictionary<string, double>();
_fileLocation = fileLocation;
}
public void FillDictionary()
{
using (var s = new StreamReader(_fileLocation))
{
int lineCount = 0;
while (!s.EndOfStream)
{
string line = s.ReadLine();
lineCount++;
string key = line.Split(';')[0];
string stringValue = line.Split(';')[1];
if (!Double.TryParse(stringValue, out double val))
throw new Exception($"Can't pass value on line: {lineCount}");
if (dict.Keys.Contains(key))
{
dict[key] += val;
}
else
{
dict.Add(key, val);
}
}
}
}
}
Here's what I recommend:
Instantiate your class's dictionary field in the class constructor. That's all you should do in the constructor. Or, go ahead and let it instantiate automatically, as you do now, and forego the constructor entirely.
Provide an Add method that replaces the code in your previous constructor. Use it to do the work of adding your dictionary/stream items. You won't need another dictionary; just add the items to your class's dictionary, directly.
Your current logic does a bit too much in the constructor, and overall there is more logic in your class than you actually need to get the job done.
This question already has answers here:
Multi-key dictionary in c#? [duplicate]
(16 answers)
Closed 1 year ago.
I want to store my player scores. I will have different worlds and worlds have different levels.
Thats why i want something like..
public static void AddScore(int world, int level, int rolls, float time, float score)
{
_scores[world][level] = new LevelScore(rolls, time, score);
}
public static LevelScore GetScore(int world, int level)
{
if (_scores.ContainsKey(world))
{
var scoresOfWorld = _scores[world];
if(scoresOfWorld.ContainsKey(level))
{
return scoresOfWorld[level];
}
}
return new LevelScore();
}
I tried it with Dictionary inside a Dictionary..
public static Dictionary<int, Dictionary<int, LevelScore>> _scores = new Dictionary<int, Dictionary<int, LevelScore>>();
but AddScore(...) leads into "KeyNotFoundException: The given key was not present in the dictionary."
I thought the key would be added if it is not existing. What is the best way for me to archive what i want easy?
You could do with a dictionary having key as combination of world and level.
var scores = new Dictionary<string, LevelScore>();
....
if (!scores.ContainsKey($"{world}_{level}"))
{
scores.Add($"{world}_{level}", value);
}
else
{
....
}
AddScore(...) leads into "KeyNotFoundException
That's because you need to add a new inner Dictionary<int, LevelScore> to the outer dictionary before you access it
dict[0][1] = ...
If there is no inner Dictionary<int, LevelScore> registered in the outer dictionary at dict[0] then you get a KeyNotFound when trying to retrieve the inner dict and set its [1]'th index to ...
You'd need a nested dictionary set code to be like:
if(!dict.TryGetValue(world, out var innerDict))
dict[world] = innerDict = new Dictionary<int, LevelScore>();
innerDict[level] = new LevelScore(rolls, time, score);
The if either retrieves the inner dictionary if it exists, or ensures one is created (and assigned to the innerDict variable) if it does not. This then means the second line can succeed (because either the innerDict is known and was retrieved, or it is new, and was set)
If you don't get on with that form, the older form also works (it just needs more lookups, but they're cheap enough that it'd be a premature optimization to obsess over them at the expense of not being able to read the code as easily)):
//ensure key exists
if(!dict.ContainsKey(world))
dict[world] = new Dictionary<int, LevelScore>();
dict[world][level] = new LevelScore(rolls, time, score);
You need to create the dictionary for the world first. It is not created automatically.
_scores[world] = new Dictionary<int, LevelScore>();
_scores[world][level] = new LevelScore(rolls, time, score);
the best way would be to use a relational data set, not dictionary. A Dictionary inside of a Dictionary is a hierarchical model. Look at this code
public class Score
{
public int World { get; set; }
public int Level { get; set; }
public int Rolls { get; set; }
public float Time { get; set; }
public float ScoreNum { get; set; }
}
public class ScoreBuilder
{
public List<Score> Scores { get; set; } = new List<Score>();
public void AddScore(int world, int level, int rolls, float time, float score)
{
var scoreObj = new Score { World = world, Level = level, Rolls = rolls, Time = time, ScoreNum = score };
Scores.Add(scoreObj);
}
public Score GetScore(int world, int level)
{
return Scores.FirstOrDefault(s=> s.World==world && s.Level==level);
}
}
You can add new score very easy and you can get any score very easy, using pure Linq.
How to use
var scores = new ScoreBuilder();
scores.AddScore(....);
scores.GetScore(...)
Can I not write values into my nested dictionary directly?
It would be nice if I could access it like this:
public Dictionary<string, Dictionary<string, Dictionary<string, int>>> planets =
new Dictionary<string, Dictionary<string, Dictionary<string, int>>>();
planets[Planet.CharacterId]["MetalMine"]["Level"] = 0;
But I'm getting:
KeyNotFoundException: The given key was not present in the dictionary.
Does this mean I got to insert my Keys after each other?
Does this mean I got to insert my Keys after each other?
Yes, you need to initialize each in order:
planets[Planet.CharacterId] = new Dictionary<string, Dictionary<string, int>>();
planets[Planet.CharacterId]["MetalMine"] = new Dictionary<string, int>();
planets[Planet.CharacterId]["MetalMine"]["Level"] = 0;
You could use collection initializer syntax here, but that won't make stuff much more readable nor maintainable.
Instead of a dictionary of dicionaries of dictionaries you seem to be better off using a class:
public class Planet
{
public List<Mine> Mines { get; set; }
}
public class Mine
{
public string Type { get; set; }
public int Level { get; set; }
}
var planets = new Dictionary<string, Planet>();
planets[Planet.CharacterId] = new Planet
{
Mines = new List<Mine>
{
new Mine
{
Type = "Metal",
Level = 0
}
};
}
It's may be helpful or Nested dictionary alternative.
Create Sample.cs script and test it.
public Dictionary<string,Tuple<string,string,string,int>> _planets = new Dictionary<string, Tuple<string,string, string, int>>();
void Start()
{
string myKey = string.Concat("1","MetalMine","Level");
if(!_planets.ContainsKey(myKey))
{
_planets.Add(myKey,Tuple.Create("1","MetalMine","Level",0));
}
Debug.Log("_planets mykey "+myKey+" ==> "+_planets[myKey].Item4);
}
Re-written everything exactly as my program has it:
class DictionaryInitializer
{
public class DictionarySetup
{
public string theDescription { get; set; }
public string theClass { get; set; }
}
public class DictionaryInit
{
//IS_Revenues data
public Dictionary<int, DictionarySetup> accountRevenue = new Dictionary<int, DictionarySetup>()
{
{ 400000, new DictionarySetup {theDescription="Call", theClass="Revenues"}},
{ 400001, new DictionarySetup {theDescription="Bill", theClass="Revenues"}},
{ 495003, new DictionarySetup {theDescription="Revenue", theClass="Revenues"}}
};
public Dictionary<int, DictionarySetup> accountExpenses = new Dictionary<int, DictionarySetup>()
{
{790130, new DictionarySetup { theDescription="Currency Hedge", theClass="Other income/expense"}},
{805520, new DictionarySetup { theDescription="Int Income", theClass="Other income/expense"}}
};
}
On my mainform:
DictionaryInit theDictionary;
btnClick() {
theDictionary = new DictionaryInit();
//Some code to loop through a datagridview
//Somemore Code
foreach (var item in theDictionary.accountRevenue)
{
int theKey = item.Key;
if (theKey == keyCode)
{
DictionarySetup theValues = item.Value;
DGVMain.Rows[rowindex].Cells[3].Value = theValues.theDescription;
DGVMain.Rows[rowindex].Cells[11].Value = theValues.theClass;
DGVMain.Rows[rowindex].Cells[12].Value = "Sale of Services";
Recording(rowindex);
}
}
}
Current work in progress:
DictionarySetup theValue;
if (theDictionary.accountExpenses.TryGetValue(keyCode,out theValue.theDescription) //[5]-> Account Type
{
//Some code to write dictionary data to the data grid view.
I'm working on making the TryGetValue and Contains(value) dictionary functions to work for now.
My current error messages are as follows:
"a property or indexer may not be passed as an out or ref parameter" when attempting the trygetvalue
and finally when trying the extension method i'm trying to create:
"Inconsistent accessibility, Dictionary<int, DictionaryInitializer.DictionarySetup> is less accessible than the method DictionaryUse<int, DictionaryInitializer.DictionarySetup>"
You have to make your field public....
Dictionary<int, DictionarySetup> accountRevenue
should be
public Dictionary<int, DictionarySetup> accountRevenue
if you want to refer to it from outside the class..
This part seems to also be missing a variable name;
public void DictionaryUse (int code, int key, Dictionary)
should be
public void DictionaryUse (int code, int key, Dictionary<int, DictionarySetup> theDictionary)
But I agree with the other comments, you seem to be re-inventing the wheel, just use the existing Dictionary utility methods
I want to use a foreach k, v pairs loop to run through a multidimensional list, and output those values elsewhere. Here is the code:
public class LogTable
{
public string FunctionName { get; set; }
public string LogTime { get; set; }
}
public class TableVars
{
public List<LogTable> loggingTable { get; set; }
public TableVars()
{
loggingTable = new List<LogTable>();
}
public void createLogList()
{
loggingTable = new List<LogTable>();
}
}
foreach( KeyValuePair<string, string> kvp in tablevars.loggingTable)
{
// output would go here. I haven't looked it up yet but I assume it's something
// along the lines of var = k var2 = v? Please correct me if I'm wrong.
}
When I run my mouse over 'foreach' I get a warning saying - 'Cannot convert type 'DataObjects.LogTable' to 'System.Collections.Generic.KeyValuePair. How can I resolve this issue, or is there a more efficient way to accomplish the same goal?
Thanks!
I should have added more context, sorry. I'm trying to return the two different values inside the properties 'FunctionName' and 'LogTime' which I have added via:
var tablevars = new TableVars();
tablevars.loggingTable.Add(new LogTable { FunctionName = errorvalue, LogTime = logtime });
To specify more accurately, the intention of the foreach k, v loop was to grab every distinct property of FunctionName and LogTime and input them into a database in SQL Server. This is why the distinction between k, v (or FunctionName, LogTime) is important. Again, please correct me if I'm wrong.
You cannot use KeyValuePair<>, because you don't enumerate a Dictionary<>. But you don't need to, simply do this:
foreach(LogTable logTable in tablevars.loggingTable)
{
// do whatever with logTable.FunctionName and logTable.LogTime
}
Either change your foreach to iterate through List<LogTable>
foreach( LogTable lt in tablevars.loggingTable)
{...}
OR
Use a KeyValuePair instead of creating class LogTable
public class TableVars
{
public Dictionary<string,string> loggingTable { get; set; }
public TableVars()
{
loggingTable = new Dictionary<string,string>();
}
public void createLogList()
{
loggingTable = new Dictionary<string, string>();
}
}
foreach( KeyValuePair<string, string> kvp in tablevars.loggingTable)
{
//loggingTagle is now a KeyValuePair
}