Dictionary on multiple layers C# - c#

everyone! Before i explain, i must say i tried to read some questions here similar to my case but couldn't get how to solve my problem, that's why i'm asking here.
Trying to put it simply, i'm working on a sort of database about Pokémon (so who knows Pokémon can understand what i'm doing) and i'm importing with json a txt file with the data i need. Until now i used a dual String dictionary structure (Dictionary String, String) to handle all data, like
"2"(this is a value used to handle the list of all Pokémon, in fact 2 is for ivysaur): {
"number": 2,
"national": "2",
"where_to_find": {
"red": "-",
"blue": "-",
"yellow": "-",
(and so on until shield, it's just a list of locations in every game where to find the pkmn)
}
and there is also more data structured like this, just as a single String key or Dictionary (dual String) like "where_to_find" as you can see above.
At the beginning of the code i created a class to handle the keys about every index in the list (every pkmn) and stated clearly
public Dictionary<String, String> where_to_find;
public String number;
public String national;
and all the rest. It works just fine. That's not the problem.
My problem is now i need to start handling the data in the txt file with a structure on multiple layers of String Keys. Since i never used such structure, i tried a simple stupid test and added in the txt file what follows (i added it at the first index, which would be Bulbasaur):
"hello": {
"hey": {
"hi": "greeting"
}
}
and above also stated that
public Dictionary<String, Dictionary<String, String>> hello;
As it recognised hello (when i write pokemon.hel, it suggests "hello" for auto-compile), but didn't recognise hey or hi, i also declared hey as a Dictionary String, String and hi as String.
BUT, this way it supposes it's pokemon.hey and pokemon.hi.
To be clear to you, think of this: suppose i want to bring up:
Messagebox.Show(pokemon.hello.hey.hi);
The messagebox in this case should write "greeting". What i can't get to do is this kind of formula, like pokemon.hello.hey.hi, or pokemon.hello["hey"["hi"]]. How can i do that? Thanks in advance for your suggestions and answers.

try hello["hey"]["hi"]
You are using the second index ([“hi”]) on the “hey” string, that expect an Int index, you should use it outside

Related

How to write every Json property into a List without its content

I have a .Json file like this:
{
"Main":{
"Funktion":{
"Sub1":"",
"Sub2":{
"something1":"",
"something2":"",
"something3":""
},
"Sub3":{
"something4":""
},
"Sub4":{
"something5":""
},
"Sub5":{
"something6":""
}
}
}
}
Now i want to write every "SubX" into a List or Array but without the text within each Sub.
So it will look like this
OUTPUT:
Printed List:
"Sub1"
"Sub2"
"Sub3"
"Sub4"
"Sub5"
I´ve already made it with converting to a String and via String handle.
Now i wonder if there is a more elegant solution for this problem.
One way to retrieve only the names of the Funktion sub-nodes is the following:
JObject.Parse(json)["Main"]["Funktion"].Children().Select(x => ((JProperty)x).Name)
With JObject.Parse we same parse the json
With ["Main"]["Funktion"] we travers through the nodes
With .Children() we ask for the sub-nodes (JTokens)
With .Select we cast the JToken to JProperty to be able to get the Name property
I want to emphasize that this approach is error-prone. It highly relies on actual structure. In order to make it more robust please consider to use TryGetValue instead of index operator.

C# Refine class property which is a List<string>

I have a class property:-
public List<string> szTypeOfFileList{get;set;}
As the name suggest, the property stores user selection of types of Files of interest (.txt, .doc, .rtf, .pdf, etc).
I am trying to assess whether there is way I could refine this List as it is being populated by user entries OR, if I should wait for all entries and then call a separate method to refine the property.
What I mean by this is, let's say a particular user input is ".doc/.docx". Currently, this would be stored in the List as a single string item. However I want it to be stored as two items separately. This will keep the code in one place and wont effect future modules and such.
private List<string> _szTypeOfFileList = new List<string>();
public List<string> szTypeOfFileList
{
get
{
return _szTypeOfFileList;
}
set
{
// Some kind of validation/refining method here ?? //
}
}
EDIT:-
Because my FileTypeList is coming from a checkboxList, I had to use a different methodology than the answer I accepted (which pointed me in the right direction).
foreach (object itemchecked in FileTypeList.CheckedItems)
{
string[] values = itemchecked.ToString().Split('/');
foreach(var item in values)
TransactionBO.Instance.szTypeOfFileList.Add(item);
}
This part of my code is in the UI class before it is passed on to the Business class.
If you know that it'll always be split with a "/" character, just use a split on the string. Including a simple bit of verification to prevent obvious duplicates, you might do something along the lines of:
string[] values = x.Split('/');
foreach (string val in values) {
if (!_szTypeOfFileList.Contains(val.ToLower().Trim())) {
_szTypeOfFileList.Add(val.ToLower().Trim());
}
}
You can also use an array of characters in place of the '/' to split against, if you need to consider multiple characters in that spot.
I would consider changing the List to something more generic. Do they really need a List ...or maybe a collection? array? enumerable ? (have a read through this link )
second, in your Set method, you'll want to take their input, break it up and add it. Here comes the question: is a list the best way of doing it ?
What about duplicate data ? do you just add it again? do you need to search for it in order to figure out if you're going to add it ?
Think about dictionary or hashtable, or any of type of collection that will help you out with your data . I would have a read through : this question (oh my ... wrong link ... nobody complained though ... so much for providing links ... :)
var extensions = userInput.Split('/').ToList();

Searching an array string with a binary search sub string

I have a file.txt containing about 200,000 records.
The format of each record is 123456-99-Text. The 123456 are unique account numbers, the 99 is a location code that I need (it changes from 01 to 99), and the text is irrelevant. These account numbers are sorted in order and with a line break in the file per ac(111111, 111112, 111113, etc).
I made a visual studio textbox and search button to have someone search for the account number. The account number is actually 11 digits long but only the first 6 matter. I wrote this as string actnum = textbox1.text.substring(0,6)
I wrote a foreach (string x in file.readline('file.txt')) with an if (x.contains(actnum)) then string code = x.substring(8,2)) statement.
The program works well, but because there are so many records if someone searches an account number that doesnt exist, or a number at the bottom of the list, the program locks up for a good 10 seconds before going to the "number not found" else statement, or taking forever to find that last record.
My Question:
Reading about binary searches I have attempted to try one without much success. I cannot seem to get the array or file to act like a legitimate binary search. Is there a way to take the 6 digit actnum from textbox1, compare it to an array substring of the 6 digit account number, then grab the substring 99 code from that specific line?
A binary search would help greatly! I could take 555-555 and compare it to the top or bottom half of the record file, then keep searching until i fine the line i need, grab the entire line, then substring the 99 out. The problem I have is I cant seem to get a proper integer conversion of the file because it contains both numbers AND text, and therefore I cant properly use <, >, = signs.
Any help on this would be greatly appreciated. The program I currently have actually works but is incredibly slow at times.
As one possible solution (not necessarily the best) you can add your record IDs to a Dictionary<string, int> (or even a Dictionary<long, int> if all record IDs are numeric) where each key is the ID of one line and each value is the line index. When you need to look up a particular record, just look in the dictionary (it'll do an efficient lookup for you) and gives you the line number. If the item is not there (non-existent ID), you won't find it in the dictionary.
At this point, if the record ID exists in the file, you have a line number - you can either load the entire file into memory (if it's not too big) or just seek to the right line and read in the line with the data.
For this to work, you have to go through the file at least once and collect all the record IDs from all lines and add them to the dictionary. You won't have to implement the binary search - the dictionary will internally perform the lookup for you.
Edit:
If you don't need all the data from a particular line, just one bit (like the location code you mentioned), you don't even need to store the line number (since you won't need to go back to the line in the file) - just store the location data as the value in the dictionary.
I personally would still store the line index because, in my experience, such projects start out small but end up collecting features and there'll be a point where you'll have to have everything from the file. If you expect this to be the case over time, just parse data from each line into a data structure and store that in the dictionary - it'll make your future life simpler. If you're very sure you'll never need more data than the one bit of information, you can just stash the data itself in the dictionary.
Here's a simple example (assuming that your record IDs can be parsed into a long):
public class LineData
{
public int LineIndex { get; set; }
public string LocationCode { get; set; }
// other data from the line that you need
}
// ...
// declare your map
private Dictionary<long, LineData> _dataMap = new Dictionary<long, LineData> ();
// ...
// Read file, parse lines into LineData objects and put them in dictionary
// ...
To see if a record ID exists, you just call TryGetValue():
LineData lineData;
if ( _dataMap.TryGetValue ( recordID, out lineData ) )
{
// record ID was found
}
This approach essentially keeps the entire file in memory but all data is parsed only once (at the beginning, during building the dictionary). If this approach uses too much memory, just store the line index in the dictionary and then go back to the file if you find a record and parse the line on the fly.
You cannot really do a binary search against file.ReadLine because you have to be able to access the lines in different order. Instead you should read the whole file into memory (file.ReadAllLines would be an option)
Assuming your file is sorted by the substring, you can create a new class that implements IComparer
public class SubstringComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return x.Substring(0, 6).CompareTo(y.Substring(0, 6));
}
}
and then your binary search would look like:
int returnedValue = foundStrings.BinarySearch(searchValue, new SubstringComparer());
Assuming the file doesn't change often, then you can simply load the entire file into memory using a structure that handles the searching in faster time. If the file can change then you will need to decide on a mechanism for reloading the file, be it restarting the program or a more complex process.
It looks like you are looking for exact matches (searching for 123456 yields only one record which is labelled 123456). If that is the case then you can use a Dictionary. Note that to use a Dictionary you need to define key and value types. It looks like in your case they would both be string.
While I did not find a way to do a better type of search, I did manage to learn about embedded resources which considerably sped up the program. Scanning the entire file takes a fraction of a second now, instead of 5-10 seconds. Posting the following code:
string searchfor = textBox1.Text
Assembly assm = Assembly.GetExecutingAssembly();
using (Stream datastream = assm.GetManifestResourceStream("WindowsFormsApplication2.Resources.file1.txt"))
using (StreamReader reader = new StreamReader(datastream))
{
string lines;
while ((lines = reader.ReadLine()) != null)
{
if (lines.StartsWith(searchfor))
{
label1.Text = "Found";
break;
}
else
{
label1.Text = "Not found";
}
}
}

What is a good substitute for a big switch-case?

I have objects called Country. At some point in the program, I want to set the field power of each object.
The power for each country is fixed and I have data for all 196 countries here on a piece of paper. My code should check, for instance, if the country's name is USA (and if so, set its power to 100) and so on.
I know I can do it with a switch-case, but what is the best, nicest, and most efficient way to do it?
You can store country-power pairs into a Dictionary<string, int> then just get the score of a particular country by using indexer:
var points = new Dictionary<string,int>();
// populate the dictionary...
var usa = points["USA"];
Edit: As suggested in comments you should store the information in external file, for example an xml would be a good choice. That way you don't have to modify the code to add or remove countries. You just need to store them into XML file, edit it whenever you need.Then parse it when your program starts, and load the values into Dictionary.You can use LINQ to XML for that.If you haven't use it before there are good examples in the documentation to get you started.
Whilst Selmans answer is right and good, it does not answer how to actually populate the Dictionary. Here is it:
var map = new Dictionary<string, int> {
{"USA", 100},
{"Germany", 110}
};
you may however also just add it as follows:
map.Add("USA", 100);
map.Add("Germany", 110);
Now you may access the value (as already mentioned by Semans):
map["USA"] = 50; // set new value for USA
int power = map["USA"]; // get new value
EDIT: As already mentioned within comments and other answers you may of course store the data within an external file or any other data-storage. Having said this you may just initialize an empty dictionary and then fill it with the Add-method previously mentioned for every record within that storage.
This is the right question to begin with, but there are a lot of things you need to learn. Many folk have given you answers to the question you asked. I'm going to be annoyingly Zen and tell you to unask the question because there is a larger problem to resolve.
Instead of hard coding this, store the related properties in an n-tuple also known as a database row and use a database engine to manage the relation between the two. And then since you are using C# it would probably be smart to learn to use LINQ. But before you do that, learn a bit of data modelling theory, because data-modelling is what you are doing.
Since you said you have "objects" called "Country", and you have tagged your question "C#", it would seem that you have two forces at work in your code. One is that having to refer to a map, however efficiently implemented, is not as cheap as referring to a member variable. On the other hand there might be some benefit to a setup where all the attributes of a country can be found in the same place as the attributes of other countries (the map-oriented solutions do address this concern). But these forces can be reconciled something like this:
class Country { // Apologies that this sketch is more C++ than C#
public:
Country(string name_, int power_);
private:
string name;
int power;
};
void MakeCountries()
{
countries.Add(new Country("USA", 50));
countries.Add(new Country("Germany", 60));
// ....
}
Do you need to update your data at runtime?
Yes? Load data from external storage into a dictionary.
No? Use a switch
Let the compiler generate dictionaries and hash-based lookups for you.
When you profiler starts screaming, explore alternative solutions.
For example, read that answer from "What is quicker, switch on string or elseif on type?".
What about making an array of Strings for storing country names in ascending order of their power. It will be more simple to implement.Then the index of each country can represent its power. This is possible, only if the power is continues counting numbers.
If its not , another siple way is to implement them as linked list. So that u will be able to change if u want. A list with 2 fields; 1for the country and other for the power

In C# can you split a string into variables?

I recently came across a php code where a CSV string was split into two variables:
list($this->field_one, $this->field_two) = explode(",", $fields);
I turned this into:
string[] tmp = s_fields.Split(',');
field_one = tmp[0];
field_two = tmp[1];
Is there a C# equivalent without creating a temporary array?
Jon Skeet said the right thing. GC will do the thing, don't you worry.
But if you like the syntax so much (which is pretty considerable), you can use this, I guess.
public static class MyPhpStyleExtension
{
public void SplitInTwo(this string str, char splitBy, out string first, out string second)
{
var tempArray = str.Split(splitBy);
if (tempArray.length != 2) {
throw new NotSoPhpResultAsIExpectedException(tempArray.length);
}
first = tempArray[0];
second = tempArray[1];
}
}
I almost feel guilty by writing this code. Hope this will do the thing.
The answer to your quite narrow question is no. C# does not provide a 'multi-assignment' capability, so you cannot extract an arbitrary set of values from anything (such as Split()) and break them out into individual named variables.
There is a workaround for a specific number of variables, by writing a parameter with out arguments. See #vlad for an answer based on that.
But why would you want to? C# provides an impressive range of features that will allow you to take apart strings and deal them with the parts in a such a wide range of different ways that the lack of 'multi-assignment' should barely be noticed.
Parsing strings usually involves other operations such as dealing with formatting errors, trimming white space, case folding. There could be less than 2 strings, or more. Requirements could change over time. When you are ready for a more capable string parser, C# will be waiting.
You may use the following approach:
-Create a class that inherits from dynamic object.
-Create a local dictionary that will store your variables values in the inherited class.
-Override the TryGetMember and TrySetMember functions to get and set values from and into the dictionary.
-Now, you can split your string and put it in the dictionary, then access your variable like:
dynamicObject.var1

Categories

Resources