C# 2 keys and a value [duplicate] - c#

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(...)

Related

Can't load a List of object (<Item>) to firebase realtime database

I'm developing a 2D game app using unity c#.
I have a list of item(object) and each item contains double Price, double Multiplier and int Level.
I'm trying to load that List to the FireBase Realtime DataBase using json inorder to save the player's peronal shop.
to try and make it look like this in the DB:
This is my Item class:
public class Item
{
public double Price {get; set;}
public double Multiplier { get; set; }
public int Level { get; set; }
public Item(double price, double multiplier, int level)
{
this.Price = price;
this.Multiplier = multiplier;
this.Level = level;
}
}
And this is my Shop class:
public class Shop
{
public List<Item> ShopList { get; set; }
public Shop()
{
this.ShopList = new List<Item>();
}
public void InitShop()
{
string[] lines = System.IO.File.ReadAllLines(#"Assets\Scripts\Data\ShopData.txt");
foreach (string line in lines)
{
string[] words = line.Split(',');
double price = Convert.ToDouble(words[0]);
double mul = Convert.ToDouble(words[1]);
int level = Convert.ToInt32(words[2]);
Item i = new Item(price, mul, level);
this.ShopList.Add(i);
}
}
If you are gonna use Json to create your save system, you might aswell use the provided JsonUtitlity by Unity. With this you can automatically create a save data object in the json format and save that.
API Reference: https://docs.unity3d.com/ScriptReference/JsonUtility.html
Youtube Tutorial (Code Monkey): https://www.youtube.com/watch?v=6uMFEM-napE
If you don't mind using a third party dll, you might want to check out Newtonsoft
https://www.newtonsoft.com/json
When you read from realtime db to Unity, it keeps the type data as keywords which is problematic to JSON.parse()
these are most likely causing issues for your code, there is a JSON.parser that handles this, at the current time I am unable to find the script in mind.
an alternative from the community is to convert it to a dictionary
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (DataSnapshot child in snapshot.Children)
{
dict.Add(child.Key, child.Value);
}
I would recommend using NewtonSoft /Json.Net for Unity. Pakcage can be found here:
https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347
Works like this:
string ConvertToJson(object obj)
{
return JsonConvert.SerializeObject(obj);
}
string ConvertFromJson<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
void Usage()
{
List<object> myList = populateListWithSomeData();
// Save it
var json = ConvertToJson(myList);
SaveInDatabase(json);
// Load it
var json = LoadFromDatabase();
myList = ConvertFromJson<List<object>>(json);
}

Writing into nested Dictionary in Unity3D

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);
}

Cannot update a single item (object) in a List C#

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);
}

How do I use dictionaries to manipulate variables based on the user's combo box selection?

Here is my dictionary code.
int numOfPlayers;
double multiplier;
Dictionary<string, Stats> games = new Dictionary<string, Stats>();
games.Add("Runescape", new Stats(1.5, 20));
games.Add("Maplestory", new Stats(1.0, 25));
games.Add("League of Legends", new Stats(1.3, 15));
Here is my stats class
class Stats
{
public double Muliplier { get; private set; }
public int NumberOfPlayers { get; private set; }
public Stats(double multiplier, int numberOfPlayers)
{
Muliplier = multiplier;
NumberOfPlayers = numberOfPlayers;
}
}
Since I don't really know much about dictionaries, how do I use it so I can set the value of my variables in the class based on what the user selected in the combo box?
Ex: If the user selects "Runescape" it would help me set the value of numOfPlayers to 20 and multiplier to 1.5.
You can get data from Dictionary using array-like indexer access:
string name = "Runescape";
Stats stat = games[name];
However, it will throw an exception when no item associated with given name exists. You can make it handle that kind of cases using TryGetValue:
Stats stat;
if(games.TryGetValue(name, out stat))
{
// stat found within the dictionary
}

Dictionary with two keys?

I am keeping track of values in a console. Two people "duel" against each other and I was using a dictionary to keep the names recorded along with damage done.
var duels = new Dictionary<string, string>();
duels.Add("User1", "50");
duels.Add("User2","34");
I'm trying to store both users in the same dictionary row, so it could be verified as User1 is dueling against User2. This way if another duel started, it would not interfere with User1 or User2.
duels.Add("KeyUser1","KeyUser2","50","34",.../*Other attributes of the duel*/);
I need two keys so I can check where the user's damage will go. The damage will always go to the other key--vice versa.
What can I do to make this work?
Thank you.
public class Duel
{
public string User1 {get; protected set;}
public string User2 {get; protected set;}
public Duel(string user1, string user2)
{
User1 = user1;
User2 = user2;
}
public HashSet<string> GetUserSet()
{
HashSet<string> result = new HashSet<string>();
result.Add(this.User1);
result.Add(this.User2);
return result;
}
//TODO ... more impl
}
Let's make some duels. CreateSetComparer allows the dictionary to use the values of the set for equality testing.
List<Duel> duelSource = GetDuels();
Dictionary<HashSet<string>, Duel> duels =
new Dictionary<HashSet<string>, Duel>(HashSet<string>.CreateSetComparer());
foreach(Duel d in duelSource)
{
duels.Add(d.GetUserSet(), d);
}
And finding a duel:
HashSet<string> key = new HashSet<string>();
key.Add("User1");
key.Add("User2");
Duel myDuel = duels[key];
You could try making a custom data type for the key:
class DualKey<T> : IEquatable<DualKey<T>> where T : IEquatable<T>
{
public T Key0 { get; set; }
public T Key1 { get; set; }
public DualKey(T key0, T key1)
{
Key0 = key0;
Key1 = key1;
}
public override int GetHashCode()
{
return Key0.GetHashCode() ^ Key1.GetHashCode();
}
public bool Equals(DualKey<T> obj)
{
return (this.Key0.Equals(obj.Key0) && this.Key1.Equals(obj.Key1))
|| (this.Key0.Equals(obj.Key1) && this.Key0.Equals(obj.Key0));
}
}
Then use a Dictionary<DualKey<string>, string>;
Something quick.
class UserScores {
public string Key { get; set; }
public int User1Score { get; set; }
public int User2Score { get; set; }
public UserScores(string username1, string username2)
{
Key = username1 + ":" + username2;
}
}
void Main()
{
var userScore = new UserScores("fooUser", "barUser");
var scores = new Dictionary<string, UserScores>();
scores.Add(userScore.Key, userScore);
// Or use a list
var list = new List<UserScores>();
list.Add(userScore);
list.Single (l => l.Key == userScore.Key);
}
Although a proper solution in my opinion would use a better thought out UserScores object that tracks that particular "duel" session.
Since a single person can be involved in at most one duel at a time, you can use a single dictionary to directly "index" both endpoints in all duels, something like this:
class Duel {
public Duel(string user1, string user2) {
Debug.Assert(user1 != user2);
User1 = user1;
User2 = user2;
}
public readonly string User1;
public readonly string User2;
public int User1Score;
public int User2Score;
}
class Program {
static void Main(string[] args) {
var dict = new Dictionary<string, Duel>();
// Add a new duel. A single duel has two keys in the dictionary, one for each "endpoint".
var duel = new Duel("Jon", "Rob");
dict.Add(duel.User1, duel);
dict.Add(duel.User2, duel);
// Find Jon's score, without knowing in advance whether Jon is User1 or User2:
var jons_duel = dict["Jon"];
if (jons_duel.User1 == "Jon") {
// Use jons_duel.User1Score.
}
else {
// Use jons_duel.User2Score.
}
// You can just as easily find Rob's score:
var robs_duel = dict["Rob"];
if (robs_duel.User1 == "Rob") {
// Use robs_duel.User1Score.
}
else {
// Use robs_duel.User2Score.
}
// You are unsure whether Nick is currently duelling:
if (dict.ContainsKey("Nick")) {
// Yup!
}
else {
// Nope.
}
// If Jon tries to engage in another duel while still duelling Rob:
var duel2 = new Duel("Jon", "Nick");
dict.Add(duel2.User1, duel); // Exception! Jon cannot be engaged in more than 1 duel at a time.
dict.Add(duel2.User2, duel); // NOTE: If exception happens here instead of above, don't forget remove User1 from the dictionary.
// Removing the duel requires removing both endpoints from the dictionary:
dict.Remove(jons_duel.User1);
dict.Remove(jons_duel.User2);
// Etc...
}
}
This is just a basic idea, you might consider wrapping this functionality in your own class...

Categories

Resources