2

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?

Zuyas
  • 182
  • 1
  • 1
  • 17

4 Answers4

1

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
{
    ....
}
S Rao
  • 21
  • 2
  • The problem with using a string is that you have to parse the key (during enumeration, for example) to get the two parts. That's why I suggested a tuple in my comment on the question. (It's only a comment because it doesn't answer the *actual* question, and neither does this.) – madreflection Dec 21 '21 at 17:19
  • Yes, agreed, using tuple is more simpler :) Thanks! – S Rao Dec 21 '21 at 17:30
1

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);
Caius Jard
  • 72,509
  • 5
  • 49
  • 80
0

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

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(...)
Serge
  • 40,935
  • 4
  • 18
  • 45