Kyle
Kyle

Reputation: 3042

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.

Upvotes: 10

Views: 15636

Answers (4)

Branko Dimitrijevic
Branko Dimitrijevic

Reputation: 52107

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

Upvotes: 2

Amy B
Amy B

Reputation: 110091

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];

Upvotes: 5

Daryl
Daryl

Reputation: 3343

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

Upvotes: 5

Razor
Razor

Reputation: 17498

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.

Upvotes: 4

Related Questions