Get item of c# dictionary by index? - c#

Im working on a project in unity, im using a dictionary to store player information and then assigning a userID int to the players which is what im attempting to make the index position of the players information in the dictionary, but i think im trying to use this system completely wrong. Currently i have this code:
public class Players : IComparable<Players> {
public int userID;
public string userName;
public int userHealth;
public GameObject userPlayer;
public Players(int newID,string Name,int Health,GameObject player){
userID = newID;
userName = Name;
userHealth = Health;
userPlayer = player;
}
public int CompareTo(Players other){
if(other == null){
return 1;
}
return userID - other.userID;
}
}
to create the Dictionary i use
private Dictionary<NetworkPlayer, Players> playerList = new Dictionary<NetworkPlayer,Players>();
to add to it i use
playerList.Add(player,new Players(playerList.Count,"Test", 100, playerObj));
I was hoping to use the playerList.Count part as a method of indexing it and then sorting it by this index to get the player i wanted back... is there a way of doing this correctly? This is my first time attempting to use dictionarys in c# and im finding it hard to understand how they work, if someone could help lead me to a working method of doing this. All i need to be able to do is to return data based off its index OR using the NetworkPlayer class.
If anyone could help lead me to a working method of doing this id be grateful, Thanks.

A standard dictionary's items are not sorted in this way. Normally, if you want to pull out the player by a specific ID, it would be better to make that the key in the dictionary, ie:
private Dictionary<int, Players> playersByID = new Dictionary<int, Players>();
private Dictionary<NetworkPlayer, Players> playersByNetwork = new Dictionary<NetworkPlayer, Players>();
Note that you could store two dictionaries, one for each form of lookup:
You could then store:
int id = nextID; // Using ID counter...
var newPlayer = new Players(id, "Test", 100, playerObj);
playersById.Add(id, newPlayer);
playersByNetwork.Add(player, newPlayer);
And fetch via:
var player = playersById[120];
Or via:
var player = playersByNetwork[netPlayer];
On a side note: I didn't use the Count as an ID, since that will fail if you ever remove players... If that is something you will never do in your system, you could go back to using the Count property for your next id.

You can also index the dictionary values by yourself enveloping the key-value pair you are interested into a KeyValuePair and associating it with an int like so:
Dictionary<int, KeyValuePair<string, int>> indexedDictionary = new Dictionary<int, KeyValuePair<string, int>>
{
{0, new KeyValuePair<string, int>("my entry", 13) },
{1, new KeyValuePair<string, int>("whatever", 5) },
{............}
};

Related

How to use dictionaries with lists as value c# unity

I am using a dictionary system to store my data and each thing I want to add to my dictionary will need to have 8 definitions. I want to know if anything like that is possible and if yes, I want to learn the codes for adding new items to the dictionary and read those items (if possible read the spesific things from the 8 items inside each list using index)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dictionary_matches : MonoBehaviour
{
/*public static Dictionary<string, List<String>> Matches_dic = new Dictionary<string, List<String>>();
dict.Add("key1", new List<int> { 1, 2, 3 });*/
public static Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("Australia", "Canberra");
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Any help would be appreciated.
It seems, that you are looking for something like this:
public class Dictionary_matches : MonoBehaviour {
public static readonly Dictionary<string, List<string>> Matches_dic =
new Dictionary<string, List<string>>() {
{"match_1", new List<string>() {"a", "b", "c"}},
{"match_2", new List<string>() {"abracadabra"}},
};
public void Update() {
// You can use in any method of the class, e.g.
Matches_dic.Add("more_match", new List<string>() {"p", "q"});
}
// Other staff
}
here we declare the dictionary and with two entries (match_1 and match_2)
When you declare such a dictionary its keys and values are empty.
Let's say I create one such dictionary
var myDict = new Dictionary<string, List<string>>()
Now I want to add a value. But remember, my value is a List<T>.
So you need to check if your key is already present, and if it is already have an instanciated list as a value (e.g. a value != null)
if(myDict.ContainsKey("myKey") && myDict["myKey"] != null)
{
// In this case you could do
mydict["myKey"].Add("myValue");
// Because you already have a list.
// And to read the first item from the list
var firstValue = mydict["myKey"][0]; // or using Linq replace [0] by First()
}
// Now if the key does not exist you need to add the key, and ideally an instantiated list.
if(!myDict.ContainsKey["myKey"])
{
// In your case we know the expected length of the list, so let's specify it.
myDict.Add("myKey", new List<string>(8))
}
You'll notice I didn't write the case where the key would exist, but the value is null.
I'll leave to your consideration if handling that particular case is needed, and if it is, you should have all necessary information in the example above.

How do you store a random key of a dictionary to refresh the dictionary when there are no longer any keys in it?

I am trying to create a dictionary wherein a randomized key is generated, and when I click on a button, this randomized key is subsequently removed from the dictionary and stored in a separate list, so that when there are no longer any keys in the dictionary, as the process has been done X number of times, the dictionary is refreshed.
I'm planning on using this dictionary to further my understanding of using random numbers in unity, practicing it through making a 'simple' quiz dictionary. I have tried creating a separate list and refreshing it (which presumably did not work because I did the code wrong I guess...) and experimenting with creating more variables to control it, which also did not work.
public class textScript : MonoBehaviour
{
public static Dictionary<int, string> questionDict = new Dictionary<int, string>();
public static Dictionary<int, string> answerDict = new Dictionary<int, string>();
public static int randomkey;
public static int removekey;
// You also need to obtain the WRONG values, so you need 'dummy' randomised values
public static int wrongkey;
public static int wrongkey1;
public static int wrongkey2;
public static int wrongkey3;
// You also need to determine the right button
public static int correctbutton;
// In order to obtain question
public static bool nextQuestion;
public void Awake()
{
// Obtains the right dictionaries
questionDict = ES2.LoadDictionary<int, string>(control.question.ToString());
answerDict = ES2.LoadDictionary<int, string>(control.answer.ToString());
}
void Start()
{
nextQuestion = true;
}
void Update()
{
noRepeatAnswers();
randomiseKeys();
GetComponent<TextMeshProUGUI>().text = questionDict[randomkey];
}
void randomiseKeys()
{
if (nextQuestion == true)
{
Random rand = new Random();
for (int i = 0; i < questionDict.Count; i++)
{
randomkey = Random.Range(1, questionDict.Count);
wrongkey = Random.Range(1, questionDict.Count);
wrongkey1 = Random.Range(1, questionDict.Count);
wrongkey2 = Random.Range(1, questionDict.Count);
wrongkey3 = Random.Range(1, questionDict.Count);
}
// Store the above value in a float array
correctbutton = Random.Range(1, 4);
}
followingQuestion();
}
void followingQuestion()
{
nextQuestion = false;
}
The idea is that when you press the 4 buttons ( which use the other integers wrongkey etc.), they will remove this random value (randomkey) from the dictionary before a new question rolls out (through the Boolean), limiting the number of questions until it refreshes, when the value of the keys = null. When I created another list and removed key, the program just went into spasm and crashed.
The purpose of a Dictionary is to store "Keyed" values, literally as KeyValuePair<,>.
From your comments, what you're asking for is a way to group Q&A pairs into categories, then remove those categories. A Dictionary can be useful in that you can name the category, and retrieve a list of Q&A pairs using that name.
public class QandA {
public string Question;
public string Answer;
}
Dictionary<string, List<QandA> > Trivia;
public InitTrivia() {
Trivia = new Dictionary<string, List<QandA>>();
Trivia.Add( "Algebra", new List<QandA> {
new QandA { Question = "What is 2 + 2?", Answer = "Four" },
new QandA { Question = "What is 6 x 3?", Answer = "Eighteen" },
});
}
If you want to randomly select a topic (a random element between zero-inclusive and element count-exclusive):
using System.Linq;
var category = Trivia.Keys.ElementAt(Random.Range(0, Trivia.Keys.Count));
From that topic, randomly select a QandA pair (a random element between zero-inclusive and element count-exclusive):
var qaList = Trivia[category];
var qaInstance = qaList[Random.Range(0, qaList.Count)];
From there you probably want to remove the QandA from the list so it doesn't get re-asked, and remove any empty lists from the dictionary.
When you want to reset, it's probably easiest to just to re-run your original init() code (which will presumably load all the QandA from a save file/serialised source)

Get specific values of a struct/List

I'm creating a game in Unity3D + C#.
What I've got at the moment: an SQL datatable, consisting of 8 columns holding a total of 3 entries and a list "_WeapList" that holds every entry (as shown below).
public struct data
{
public string Name;
public int ID, dmg, range, magazin, startammo;
public float tbtwb, rltimer;
}
List<data> _WeapList;
public Dictionary<int, data>_WeapoList; //probable change
[...]
//reading the SQL Table + parse it into a new List-entry
while (rdr.Read())
{
data itm = new data();
itm.Name = rdr["Name"].ToString();
itm.ID = int.Parse (rdr["ID"].ToString());
itm.dmg = int.Parse (rdr["dmg"].ToString());
itm.range = int.Parse (rdr["range"].ToString());
itm.magazin = int.Parse (rdr["magazin"].ToString());
itm.startammo = int.Parse (rdr["startammo"].ToString());
itm.tbtwb = float.Parse(rdr["tbtwb"].ToString());
itm.rltimer = float.Parse(rdr["rltimer"].ToString());
_WeapList.Add(itm);
_WeapoList.Add(itm.ID, itm);//probable change
}
Now I want to create a "Weapon"-Class that will have the same 8 fields, feeding them via a given ID
How do I extract the values of a specific item (determined by the int ID, which is always unique) in the list/struct?
public class Weapons : MonoBehaviour
{
public string _Name;
public int _ID, _dmg, _range, _magazin, _startammo;
public float _tbtwb, _rltimer;
void Start()
{//Heres the main problem
_Name = _WeapoList...?
_dmg = _WeapoList...?
}
}
If your collection of weapons may become quite large or you need to frequently look up weapons in it, I would suggest using a Dictionary instead of a List for this (using the weapon ID as the key). A lookup will be much quicker using a Dictionary key than searching through a List using a loop or LINQ.
You can do this by modifying your code to do this as follows:
public Dictionary<int, data>_WeapList;
[...]
//reading the SQL Table + parse it into a new List-entry
while (rdr.Read())
{
data itm = new data();
itm.Name = rdr["Name"].ToString();
itm.ID = int.Parse (rdr["ID"].ToString());
itm.dmg = int.Parse (rdr["dmg"].ToString());
itm.range = int.Parse (rdr["range"].ToString());
itm.magazin = int.Parse (rdr["magazin"].ToString());
itm.startammo = int.Parse (rdr["startammo"].ToString());
itm.tbtwb = float.Parse(rdr["tbtwb"].ToString());
itm.rltimer = float.Parse(rdr["rltimer"].ToString());
_WeapList.Add(itm.ID, itm);//probable change
}
Then, to access elements on the list, just use the syntax:
_WeapList[weaponID].dmg; // To access the damage of the weapon with the given weaponID
Guarding against invalid IDs:
If there's a risk of the weaponID supplied not existing, you can use the .ContainsKey() method to check for it first before trying to access its members:
if (_WeapList.ContainsKey(weaponID))
{
// Retrieve the weapon and access its members
}
else
{
// Weapon doesn't exist, default behaviour
}
Alternatively, if you're comfortable using out arguments, you can use .TryGetValue() instead for validation - this is even quicker than calling .ContainsKey() separately:
data weaponData;
if (_WeapList.TryGetValue(weaponID, out weaponData))
{
// weaponData is now populated with the weapon and you can access members on it
}
else
{
// Weapon doesn't exist, default behaviour
}
Hope this helps! Let me know if you have any questions.
Let specificWeapon be a weapon to be searched in the list, then you can use the following code to select that item from the list of weapons, if it is not found then nullwill be returned. Hope that this what you are looking for:
var selectedWeapon = WeapList.FirstOrDefault(x=> x.ID == specificWeapon.ID);
if(selectedWeapon != null)
{
// this is your weapon proceed
}
else
{
// not found your weapon
}
You can use LINQ to search specific object through weaponId
var Weapon = _WeapList.FirstOrDefault(w=> w.ID == weaponId);

Only Add Unique Item To List

I'm adding remote devices to a list as they announce themselves across the network. I only want to add the device to the list if it hasn't previously been added.
The announcements are coming across an async socket listener so the code to add a device can be run on multiple threads. I'm not sure what I'm doing wrong but no mater what I try I end up with duplications. Here is what I currently have.....
lock (_remoteDevicesLock)
{
RemoteDevice rDevice = (from d in _remoteDevices
where d.UUID.Trim().Equals(notifyMessage.UUID.Trim(), StringComparison.OrdinalIgnoreCase)
select d).FirstOrDefault();
if (rDevice != null)
{
//Update Device.....
}
else
{
//Create A New Remote Device
rDevice = new RemoteDevice(notifyMessage.UUID);
_remoteDevices.Add(rDevice);
}
}
If your requirements are to have no duplicates, you should be using a HashSet.
HashSet.Add will return false when the item already exists (if that even matters to you).
You can use the constructor that #pstrjds links to below (or here) to define the equality operator or you'll need to implement the equality methods in RemoteDevice (GetHashCode & Equals).
//HashSet allows only the unique values to the list
HashSet<int> uniqueList = new HashSet<int>();
var a = uniqueList.Add(1);
var b = uniqueList.Add(2);
var c = uniqueList.Add(3);
var d = uniqueList.Add(2); // should not be added to the list but will not crash the app
//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"Happy");
dict.Add(2, "Smile");
dict.Add(3, "Happy");
dict.Add(2, "Sad"); // should be failed // Run time error "An item with the same key has already been added." App will crash
//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<string, int> dictRev = new Dictionary<string, int>();
dictRev.Add("Happy", 1);
dictRev.Add("Smile", 2);
dictRev.Add("Happy", 3); // should be failed // Run time error "An item with the same key has already been added." App will crash
dictRev.Add("Sad", 2);
Just like the accepted answer says a HashSet doesn't have an order. If order is important you can continue to use a List and check if it contains the item before you add it.
if (_remoteDevices.Contains(rDevice))
_remoteDevices.Add(rDevice);
Performing List.Contains() on a custom class/object requires implementing IEquatable<T> on the custom class or overriding the Equals. It's a good idea to also implement GetHashCode in the class as well. This is per the documentation at https://msdn.microsoft.com/en-us/library/ms224763.aspx
public class RemoteDevice: IEquatable<RemoteDevice>
{
private readonly int id;
public RemoteDevice(int uuid)
{
id = id
}
public int GetId
{
get { return id; }
}
// ...
public bool Equals(RemoteDevice other)
{
if (this.GetId == other.GetId)
return true;
else
return false;
}
public override int GetHashCode()
{
return id;
}
}

Working around my Two-Dimensional Array

I just wanna ask what best way to work around a Two-Dimensional Array (2 Columns) which would store: CandidateName and their respective VoteCount.
What I want exactly to do is to, accept an input from the user say: VOTE John 10 wherein John is the name of the candidate and 10 is the votes that he wanna give him. So I need to store {John, 10} into my array. However, after this my program would once again ask the user for votes so if I enter VOTE Doe 15, the entry {Doe, 15} would then be added to the array. If the user enters VOTE John 2, my array needs to be updated and thus the new value would be {John, 12}.
Currently I use two arraylists: CandidateName and VoteCount and I just rely on their index for pairing. However, this isn't really reliable so I'm trying to find another way on how to solve this. However, I'm not really a big fan of multi-dimensional arrays.
Can someone please point me out to a good way on how to achieve this?
public class VoteManager
{
public Dictionary<string, int> Votes { get; private set; }
public VoteManager
{
Votes = new Dctionary<string, int>();
}
public void AddVotes(string name, int voteCount)
{
int oldCount;
if (!Votes.TryGetValue(name, out oldCount))
oldCount = 0;
Votes[name] = oldCount + voteCount;
}
You should use an Associative Array. In the case of C#, such a collection is the Dictionary.
var votes = new Dictionary<string, int>();
votes["John"] = 10;
votes["Bob"] = 20;
votes["John"] = 15; // replaces earlier setting
If you want to add to the exisiting vote, you will need to check if there is an existing value:
private Dictionary<string, int> votesByPeep; // initialized in constructor
private void AddVotes(string peep, int votes)
{
if (this.votesByPeep.ContainsKey(peep)
{
this.votesByPeep[peep] += votes;
}
else
{
this.votesByPeep[peep] = votes;
}
}
Why don't you define a struct/class with two properties, Name and VoteCount. Then you only need one array.
EDIT:
I suggested this because there may be additional operations or properties you want to add to Candidates. If all you need is an association between these two values, a dictionary is the correct solution.
It sounds like a much better solution here is to use a Dictionary<TKey, TValue>. A dictionary / hashtable is ideal for a scenario where you're pairing a value (vote count) with a given key (user name). It makes for very easy update and lookup scenarios
class Container {
private Dictionary<string, int> m_voteMap = new Dictionary<string, int>();
public void SetVote(string user, int votes) {
m_voteMap[user] = votes;
}
public int GetVotes(string user) {
int votes;
if (!m_voteMap.TryGetValue(user, out votes)) {
votes = 0;
}
return votes;
}
}
You can use a dictionary from strings (names) to int (votes), this will give you the {name, votes} pair and a nice quick lookup
Create a class called CandidateVotes, and store that in a List<CandidateVotes> collection.
public class CandidateVotes
{
public string Name {get; set;}
public int Votes {get; set;}
}
Dictionary<string, int> is your friend
This sounds like a good candidate for a Dictionary<T,U>. In this case, Dictionary<string,int>, with the key being the candidate, and the value being the vote count.
// Create dictionary as:
Dictionary<string, int> votes = new Dictionary<string, int>();
You could then make some routines like the following:
void AddVotes(string candidate, int numberOfVotes)
{
if (this.votes.Contains(candidate))
{
// Update the "10 to 12" in your scenario
int current = this.votes[candidate];
current += numberOfVotes;
this.votes[candidate] = current;
}
else
this.votes[candidate] = numberOfVotes; // First time a candidate is used...
}
When you want to list out the votes per candidate, you can do something like:
foreach(var pair in this.votes)
{
Console.WriteLine("Candidate {0} has {1} votes.", pair.Key, pair.Value);
}

Categories