Get specific values of a struct/List - c#

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

Related

Optimize code for output multiple int variables into String

I have a few int variables declared:
[SerializeField] private int currentHP, currentMP, maxHP, maxMP, attack, intelligence, defense, speed, critChance;
And I would like to output them to a text that says "Your stat is xx". I have used the following code and it works:
if (maxHP > 0)
{
stats += string.Format("\n Your stat is {0}", maxHP);
}
However, I would like to know if there is a way to avoid copy paste this code for each int. Is it possible?
Thanks!
First, put every stat into an array:
int[] statsArray = { currentHP, currentMP, maxHP, maxMP, attack, intelligence, defense, speed, critChance };
Then, use a LINQ query to create a string for each stat. Note that you can make use of string interpolation:
var strings = from stat in statsArray
where stat > 0 // filters out the non-positive stats, like your if statement does
select $"\nYour stat is {stat}."; // string interpolation
Then, join the strings together using string.Concat:
stats = string.Concat(strings);
An easy way, without declaring your variables in another way, would be to create a function
private string GetStatString(int stat) {
if (stat> 0)
{
return string.Format("\n Your stat is {0}", maxHP);
}
return "";
}
And in your code:
stats += GetStatString(maxpHP) + GetStatString(maxMp); // etc
If you can add these into an array, then the work gets much easier, and you can combine with #Sweepers answer.
Sweepers answer is probably the best approach.
However, if you really don't want to repeat yourself, not even the field names, you could use reflection to get all the ints in the class.
var strings = GetType().GetFields(BindingFlags.NonPublic|BindingFlags.Instance);
.Select(field => field.GetValue(this))
.OfType<int>()
.Where(value => value > 0)
.Select(value => $"Your stat is {value}");
var concatenated = string.Join("\n", strings);
If you want to frequently iterate over all stats, perhaps it's better to use a dictionary, or a list of string and int tuples.
You can also use System.Reflection in case you need to get the values during runtime without the need to put the fields in an array and to avoid adjusting your code to the print feature :
public string GetStats()
{
string stats = string.Empty;
// Get a list of FieldInfo objects.
var fieldsInfo = GetType().GetFields(BindingFlags.NonPublic
| BindingFlags.Instance)
.Where(field => field.FieldType == typeof(int)).ToList();
foreach (var field in fieldsInfo)
{
int fieldValue = (int)field.GetValue(this);
if (fieldValue > 0)
{
stats += string.Format("\n Your stat is {0}", fieldValue);
}
}
return stats;
}

Setting the size of an array using input from textbox in win forms

I am trying to use the input given in Form1 to define the size of the array but am getting an error saying that a field initializer cannot use a non-static field. This always worked for me in CLI but for some reason it is not working here.
namespace Class_Grade_Register
{
public partial class Student_Input : Form
{
int number_Of_Students = 0;
int counter = 0;
string[] studentName = new string[number_Of_Students];
int[] sfcGrade = new int[number_Of_Students]; // this is where I am getting the error. number_Of_Students is being underlined in red.
int[] csGrade = new int[number_Of_Students];
int[] sdtGrade = new int[number_Of_Students];
int[] ddoocpGrade = new int[number_Of_Students];
public Student_Input()
{
InitializeComponent();
}
public void Set_Number_Of_Students(int value)
{
number_Of_Students = value;
}
The idiomatic way of representing complex data (basically, anything where you have to connect two integers or strings together somehow) is by creating classes. Beginners are strangely reluctant to do this, instead creating arrays or multiple variables like student1Name, student2Name and so on.
Don't be like that, classes are a fundamental concept in object-oriented programming and you have got to become comfortable with them. Start with something like this:
public class Student
{
public string Name { get; }
public int Grade { get; }
public Student(string name, int grade)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
Grade = (grade > 0) ? grade : throw new ArgumentException(nameof(grade));
}
}
Add new properties as you need them. Also get in the habit of throwing exceptions when you detect something is wrong. Beginners have a bad habit of trying to hide exceptions. Doing that puts errors in your data and prevents you from finding them.
Now you can start creating students:
var me = new Student("Dour High Arch", 10);
If you want to create a bunch of students, use a list or collection:
var students = new List<Student>().
students.Add(me);
You have another bad design in your code; asking for the total number of students before adding them. This causes problems like null references if not as many students get added as your total number, or out-of-range exceptions if more students get added than your total number. Moreover in many real-world situations you won't know how many students or whatever there are until you've added them all. Use a design more like this:
while (true)
{
var nextStudent = GetStudentFromSomewhere();
if (nextStudent == null)
break;
students.Add(nextStudent);
}
Arrays are designed for collections which must always contain a set number of elements and cannot change, like the months of a year. If you expect the number of elements to change you should not use an array.
try making number_Of_Students static, something like this:
static int number_Of_Students = 0;
and also you need to modify your Set_Number_Of_Students method to something like this:
public void Set_Number_Of_Students(int value)
{
number_Of_Students = value;
studentName = new string[number_Of_Students];
sfcGrade = new int[number_Of_Students];
csGrade = new int[number_Of_Students];
sdtGrade = new int[number_Of_Students];
ddoocpGrade = new int[number_Of_Students];
}
int number_of_students = 0;
int[] sfcGrade;
public void Set_Number_of_Students(int value)
{
number_of_students = value;
sfcGrade = new int[number_of_students];
}
As described here the compiler will not necessarily initialize number_of_students before sfcGrade leading to the index size of sfcGrade being an undefined int.

Multiple Nested JSON information - C# Process

apologies if I'm doing something wrong, this is my first post.
I'm currently working with C# and want to save a bunch of data out to a JSON file and load it back, but I'm having trouble figuring out how to get it in the following format.
// Primary ID
001
{
// Secondary ID
01
{
// Tertiary ID
01
{
string: "this is some information.",
int: 9371
}
}
// Secondary ID
02
{
// Tertiary ID
01
{
string: "blah blah blah.",
int: 2241
}
}
}
I'd essentially like to be able to call up information with a particular set of IDs for example 001-02-01 which would return a string ("blah blah blah.") and an int (2241).
The reason I want to go about it like this instead of just having one longer ID is so that when the JSON file becomes very large, I'm hoping to be able to speed up the search for information by passing each ID in turn.
If that makes no sense and it would be equally as fast to just pass in one longer ID and not be bothered by this whole nested ID segments concept then please let me know!
If, however what I'm thinking is correct and it would help the speed of finding particular data by structuring it out like this, how would I go about doing that? With nested C# classes in arrays?
The most simple way and efficient way would be to have all data as same type. Currently, you seem to go for each object is of type of the given id:
{
"01":{},
"02" :{}
}
this will not go too well if trying to use a serializable class.
I would recommend the following:
{
"items" : [
{"id":"01" }, { "id":"02" },...
]
}
Then you can serialize/deserialize easily with
[Serializable]
public class Item
{
public string id = null;
}
[Serializable]
public class RootObject
{
public List<Item> items = null;
}
and then in Unity:
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
}
if you want to speed up the fetching and your collection is large, convert to dictionary.
Dictionary<string, Item> dict = null;
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
this.dict = new Dictionary<string,Item>();
foreach(Item item in ro.items){
Item temp = temp;
this.dict.Add(item.Id, temp);
}
ro = null;
}
Now you can access real fast.
Item GetItem(string id)
{
if(string.IsNullOrEmpty(id) == true){ return null; }
Item item = null;
this.dict.TryGetValue(id, out item);
return item;
}
If you end up storing millions of records in your file and want to start doing something more performant it would be easier to switch to a decent document database like MongoDB rather than trying to reinvent the wheel.
Worry about writing good standard code before worrying about performance problems that don't yet exist.
The following example is not in your language of choice but it does explain that JSON and arrays of 1,000,000 objects can be searched very quickly:
const getIncidentId = () => {
let id = Math.random().toString(36).substr(2, 6).toUpperCase().replace("O", "0")
return `${id.slice(0, 3)}-${id.slice(3)}`
}
console.log("Building array of 1,000,000 objects")
const littleData = Array.from({ length: 1000000 }, (v, k) => k + 1).map(x => ({ cells: { Number: x, Id: getIncidentId() } }))
console.log("Getting list of random Ids for array members [49, 60, 70000, 700000, 999999]")
const randomIds = ([49, 60, 70000, 700000, 999999]).map(i => littleData[i].cells.Id)
console.log(randomIds)
console.log("Finding each array item that contains a nested Id property in the randomIds list.")
const foundItems = littleData.filter(i => randomIds.includes(i.cells.Id))
console.log(foundItems)

C# - Nested Array/Data structures

Recently, I have been getting into C# (ASP.NET) and moving on from PHP. I want to achieve something like this:
mainArray (
array 1 (
'name' => 'example'
),
array 2 (
'name' => 'example2'
)
);
I know that you can use an Array in C# however, you must indicate the length of the Array before doing so which is where the problem is.
I want to loop through a Database in a Class function which returns an Array of all the columns, ie:
id, username, email.
I have tried:
public Array search_bustype(string match, string forthat)
{
db = new rkdb_07022016Entities2();
var tbl = (from c in db.tblbus_business select c).ToArray();
List<string> List = new List<string>();
int i = 0;
foreach (var toCheck in tbl)
{
if (toCheck.BusType.ToString() == match)
{
if (forthat == "Name")
{
List.Add(toCheck.Name);
}
if (forthat == "Address")
{
}
}
i++;
}
return List.ToArray();
}
But as you can see, I am having to only return the single column because the List is not multidimensional (can't be nested).
What can I use to solve this issue? I have looked at some links:
C# Arrays
StackOverflow post
But these again are an issue for my structure since I don't know how many index's I need in the Array when declaring it - The Database grows everyday.
Thanks in advance.
Try something like this. First, define a class for your business model.
public class Person
{
public string Name {get;set;}
public string Address {get;set;}
}
Then use a generic list instead of a string list.
public Person[] search_bustype(string match, string forthat)
{
var db = new rkdb_07022016Entities2();
List<Person> personList = new List<Person>();
foreach (var toCheck in db.tblbus_business.Where(b => b.BusType.ToString() == match))
{
var model = new Person { Name = toCheck.Name, Address = toCheck.Address };
personList.Add(model);
}
return personList.ToArray();
}
I'm not sure what you are trying to do with the forthat variable.
You can use a list of lists
IList<IList<string>> multiList;

List won't add string Array

I created the following class:
class TrdRamValue
{
double Value = 0.0;
TrdState State = TrdState.Ok;
DateTime dt = DateTime.UtcNow;
}
I then created a list with this class to store the information:
List<TrdRamValue> DMSrows = new List<TrdRamValue> ();
And I use the following inside a Handler to constantly insert values every second:
string[] value = new string[3];
value[0] = val;
value[1] = val.Error.ToString ();
value[2] = val.Time.ToString ();
DMSrows.AddRange (value);
But in code it keeps saying that I have an error in my argument, that I can't convert string[] to System.Collections.Generic.IEnumerable.
I'm completely lost on this one...
ANSWER:
It was just a minor error from my part, and I also took huMpty duMpty suggestion since he's completely right, I don't need that string array.
All I had to do was make the class and the variables inside public in order to do what huMpty duMpty told me.
public class TrdRamValue
{
public double Value = 0.0;
public TrdState State = TrdState.Ok;
public DateTime dt = DateTime.UtcNow;
}
Then apply huMpty duMpty suggestion:
TrdRamValue value = new TrdRamValue() ;
value.Value = val;
if (!val.Error) {
value.State = TrdState.Ok;
}
else if (val.Error) value.State = TrdState.Error;
value.dt = val.Time;
DMSrows.Add (value);
Your List is not a List<string> but a List<TrdRamValue>. Therefore, you cannot add strings to the list. You can only add instances of TrdRamValue, or, in the case of AddRange, an IEnumerable (such as an array) of TrdRamValue.
So you can do this:
TrdRamValue toAdd = new TrdRamValue { Value = val, State = ..., dt = ... };
dmsRows.Add(toAdd);
(btw naming a variable DMSRows does not fit with the .net naming conventions).
You're trying to add strings to a list of TrdRamValue objects. Your list is type-safe, which means you are only allowed to add TrdRamValue objects to it.
Not sure why you need string array here.
Also you don't need List.AddRange here since you adding one item. You can use List.Add
DMSrows.Add(new TrdRamValue{
Value =val,
State =val.Error,
dt =val.Time
});

Categories