Sort txt file with JSON - c#

I'm making a Scoreboard for a game. I'm developing but have no experience with json.
So far I have made it so it adds a name with a score into a plain text file and also displaying this in game.
Now I want to sort it so that the name with the lowest score goes to the top.
Here is my code:
[WebMethod]
public static void Score(String gamescore, string loginname)
{
List<Highscore> Myhighscores = new List<Highscore>();
string hs = File.ReadAllText(#"c:\temp\hs.txt");
Myhighscores = JsonConvert.DeserializeObject<List<Highscore>>(hs);
Myhighscores.Add(new Highscore { Score = gamescore, Name = loginname });
string Jstr = JsonConvert.SerializeObject(Myhighscores);
File.WriteAllText(#"c:\temp\hs.txt", Jstr);
}
public void displayScore()
{
using (StreamReader r = new StreamReader(#"c:\temp\hs.txt"))
{
string json = r.ReadToEnd();
List<Highscore> items = JsonConvert.DeserializeObject<List<Highscore>>(json);
foreach (Highscore score in items)
{
lblHighscore.Text += $"{score.Name} > {score.Score} {"Turns"}<br/>";
}
}
}
I have tried a lot of things but nothing seems to be working.
Does anyone know how I can achieve this?
p.s Highscore contains 2 public strings called Name and Score.

You need to sort your object before writing it again back in file.
Myhighscores.Sort((x, y) => string.Compare(x.Score, y.Score));

Its pretty easy all i needed to do is change gamescore to a int and add the following code:
List<Highscore> SortedList = items.OrderBy(o => o.Score).ToList();

Related

How to get character info from a file?

I created a file which named by a given name by user, and add some stats in it for example : healt, power, speed, inventory like so. Now, i want to create a function that get stats from given path. It need find the file from given name and get the stat.
I tried re-read the file as note/json so on and it didnt work very well. I want to get variables like :
if(inventory.Contains("Bread"))
Note : I tried to save files as .json but it saved as unknown note i dont know how and why.
...
...
CharacterData Character = new CharacterData()
{
health = 100,
power = 100,
speed = 100,
dexterity = 100,
hungry = 100,
weapon = "Fist",
inventory = new List<string>()
{
"Bread"
}
};
string stringjson = JsonConvert.SerializeObject(Character);
File.WriteAllText(characterPath, stringjson);
}
public int GetCharInfo(string charName,string stat)
{
//return (stat value)
}
you can do something like this:
CharacterData Character = new CharacterData()
{
health = 100,
power = 100,
speed = 100,
dexterity = 100,
hungry = 100,
weapon = "Fist",
inventory = new List<string>()
{
"Bread"
}
};
string stringjson = JsonConvert.SerializeObject(Character);
string path = #"C:\DEV\StackOverflow\";
string characterPath = path + "johnny.json";
File.WriteAllText(characterPath, stringjson);
public int GetCharInfo(string charName, string stat)
{
string json = File.ReadAllText(path + $"{charName}.json");
JObject obj = JObject.Parse(json);
return (int)obj[stat];
}
now if you call:
GetCharInfo("johnny", "power")
you will get the value:
100
also, if you want to see if the key exists you can use the ContainsKey method on JObject like such:
if(obj.ContainsKey(stat))
return (int)obj[stat];
UPDATE
if you want to get a "stat" of different type I suggest doing this:
have this method that gets the character as a json object
public JObject GetCharacterJSON(string charName){
string json = File.ReadAllText(path + $"{charName}.json");
return JObject.Parse(json);
}
now you can load this inside a variable
JObject johnny = GetCharacterJSON("johnny);
And you can get the inventory property like such:
List<string> inventory = johnny["inventory"].ToObject<List<string>>();
As a suggestion, from my point of view if I were you I would try to deserialize the json string into a CharacterData object and then use that like this:
public CharacterData GetCharacterData(string charName){
string json = File.ReadAllText(path + $"{charName}.json");
return JsonConvert.DeserializeObject<CharacterData>(json);
}
CharacterData characterData = GetCharacterData("johnny");
int power = characterData.power; //100
List<string> inventory = characterData.inventory; //["Bread"]
if(inventory.Contains("Bread"){
//do something
}
Note: I didn't take into account error handling in all examples for when the file does not exist or property does not exist etc. I've just outlined the general principle in order to be simple to understand.
Thanks
I guess begin with
File.ReadAllText(path)
Example of usage and documentation
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltext?view=net-6.0
Then you can covert the result to JSON and extract the information that you want

Better method for loading a list of items to be parsed into two, and calling the matching item by index?

I have a .txt file with a list of items (u.s. state and capitals) going down such as Arizona:Phoenix Arkansas:Little Rock California:Sacramento. I'm going to be importing that list, but only want to display the States in a Combobox. After that, if comboBox1.Items[0] is selected, I want it to get the corresponding item that was initially parsed along with it after the : delimiter. My initial solution was to create a class to hold both values, and hold them in a List and compare the index from the Combobox to that of the List to get the matching value. I feel like this might be overkill and I am over thinking it for something as simple as a combobox where the data won't be subjected to any complex manipulations. Would there be a simpler method/datatype to use to do this? I just want to get the corresponding value after the : delimiter from the Combobox index that was parsed when it was first loaded.
First of all build your classes of State & Capital like this:
public class State
{
public string stateName { get; set; }
public Capital capital { get; set; }
}
public class Capital
{
public string capitalName { get; set; }
}
Read the text file, generate a list and populate the ComboBox like this:
List<State> list = new List<State>();
var file = File.ReadAllLines(FilePath).ToList();
foreach (var item in file)
list.Add(new State()
{
stateName = item.Split(':')[0],
capital = new Capital() { capitalName = item.Split(':')[1] }
});
StatesCB.DataSource = list.Select(x => x.stateName).ToList();
And within your ComboBoxIndexChange eventHandler, get the Capital based on the State.
private void Sates_SelectedIndexChanged(object sender, EventArgs e)
{
capital.Text = list.Where(x => x.stateName == StatesCB.SelectedValue)
.Select(x => x.capital.capitalName).FirstOrDefault();
}
It works and address your problem perfetcly.
You can try this:
I assume your text file contains the following lines:
Arizona:Phoenix
Arkansas:Little
Rock California:Sacramento
On your code:
List<string> lstResult = new List<string>();
using (StreamReader sr = new StreamReader(#"C:\Stack\file.txt"))
{
string line = string.Empty;
while ((line = sr.ReadLine()) != null)
{
//Here I am getting the second part of splitted string which is your requirement
lstResult.Add(line.Split(':').Select(x=>x).Skip(1).SingleOrDefault().ToString());
}
}
comboBox1.DataSource = lstResult;
This will produce:

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

How to take the last line of the same Name from text file?

i'm trying to load some data into gridview, sorry if my phrasing is bad or weird, but what i mean exactly is, lets say my text file has this data
Name1,123
Name1,133
Name1,143
Name2,533
Name2,233
Name2,973
Name2,313
Name3,533
Name3,233
Name3,973
Name3,213
...
and i only want to take the last line of Name1, Name2, Name3 and inserting to gridview
Name1,143
Name2,313
Name3,213
subsequently, i'll be trying to update the gridview cells, if further down in the text file there is the same Name again. but yeah. i'm trying to even get this part first.
so i have been trying to use foreach to try to readline and like add them all in and then removing the ones that have the same Name. but it's not removing any of it.
my codes currently is
public class LiveData
{
public string Name { get; set; }
public double Price { get; set; }
public static List<LiveData> LoadDataFromFile(string path)
{
var data = new List<LiveData>();
foreach (var line in File.ReadAllLines(path))
{
var dataLine = line.Split(',');
data.Add(new LiveData
{
Name = dataLine[0],
Price = Convert.ToDouble(dataLine[1])
});
}
return data;
}
}
and
private void btnLoadLDgridView_Click(object sender, EventArgs e)
{
StreamReader file = new StreamReader(tbTSSelectFile.Text);
List<LiveData> list = LiveData.LoadDataFromFile(tbTSSelectFile.Text);
List<LiveData> lst2 = LiveData.LoadDataFromFile(tbTSSelectFile.Text);
while (file.ReadLine() != null)
{
string name = lst2[0].Name;
foreach (LiveData item in list)
{
if (item.Name == name)
{
lst2.Add(item);
}
}
foreach (LiveData item in list)
{
if (item.Name == name)
{
lst2.Remove(item);
}
}
gvLiveData.DataSource = lst2;
}
i'm thinking maybe .Last can be used but i am not sure how to implement it.
the text file data is supposedly Live Stocks data, hence the need to update later on.
thanks in advance :)
After you've parsed your file into the data variable, you could try using Linq:
var lastItems = data.GroupBy(d => d.Name).Select(g => g.Last());
This utilizes the GroupBy and Last operators.

Splitting a list<> that each item contains comma separated strings and formatting it out

I have a simple class like this:
class QuickReport
{
public string DeviceName { get; set; }
public string GroupName { get; set; }
public string PinName { get; set; }
public override string ToString()
{
return DeviceName + "," + GroupName + "," + PinName;
}
}
Later I make a list of items with this class:
List<QuickReport> QR = new List<QuickReport>();
Later in my program it will fill up and when I save it in a text file it will be like this example:
HBM\D1,GND,10
HBM\D1,GND,12
HBM\D1,NT_IOp,115
HBM\D1,NT_IOp,117
HBM\D2,GND,8
HBM\D2,NT_IOp,115
HBM\D2,NT_IOp,116
Now I want to make a function to save the text file in more readable manner. That is formatting it by DEVICE, GROUPS and PINS. So the above example would result in:
HBM\D1
GND: 10, 12
NT_IOp: 115, 117
HBM\D2
GND: 8
NT_IOp: 115, 116
can you please help and give some ideas?
Thanks!
var query = QR.ToLookup(i=>i.DeviceName, i => new {i.GroupName, i.PinName})
.Select(i=>
new {DeviceName = i.Key,
Groups = i.ToLookup(g=>g.GroupName, g=>g.PinName)});
var sb = new StringBuilder();
foreach ( var device in query)
{
sb.AppendLine(device.DeviceName);
foreach ( var gr in device.Groups)
{
sb.Append(gr.Key + ": ");
sb.Append(String.Join(", ", gr.ToArray()));
sb.AppendLine();
}
sb.AppendLine();
}
var stringToWrite = sb.ToString();
As i understand you have tree structure, where Device have child Groups, and Groups have child pins.
You can create custom classes like this:
class Group
{
string Name;
//pins that belong to this group
List<string> pins;
}
class Device
{
string Name;
//groups that belong to this device
List<Group> Groups;
}
And than just collect it to List<Device> and serialize it using XML Serialization.
This isn't complete, but it should give you enough to go on. You'll still need to add your newlines, and remove trailing commas, etc.
// Make your key the device name
var qrHash = new Dictionary<string, List<QuickReport>>();
// Populate your QR Dictionary here.
var output = new StringBuilder();
foreach (var keyValuePair in qrHash)
{
output.Append(keyValuePair.Key);
var gnd = new StringBuilder("GND: ");
var nt = new StringBuilder("NT_IOp: ");
foreach (var qr in keyValuePair.Value)
{
gnd.Append(qr.GroupName);
nt.Append(qr.PinName);
}
output.Append(gnd);
output.Append(nt);
}
How about using the XmlSerializer to serialize and deserialize your class? This should provide some readable output.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx
The quickest ways I can think of to do this would either be to loop over the List<> 3 times, eachtime checking on a seperate accessor, writing it out to a StringBuilder, then returning StringBuilder.ToString() from the function.
Or, you could use 3 stringbuilders to hold each accessor type, then push all 3 from the function on return.

Categories