Using Dictionaries to assign values to variables - c#

I have the following code which is intended, as an example, to take a fruitName and assign it to the next available unused "name#" string variable (one that does not already have a fruit name assigned to it).
Wanting to avoid using nested IF statements, i am trying to use a Dictionary as follows.
public static void AssignFruitToNextAvailableSlot(string fruitName)
{
string NextEmptyNameSlot = "";
string Name1 = "Apple";
string Name2 = "Orange";
string Name3 = "Tomato";
string Name4 = "";
string Name5 = "";
string Name6 = "";
string Name7 = "";
string Name8 = "";
string Name9 = "";
Dictionary<string, string> nameSlots = new Dictionary<string, string>()
{
{"Slot1", Name1},
{"Slot2", Name2},
{"Slot3", Name3},
{"Slot4", Name4},
{"Slot5", Name5},
{"Slot6", Name6},
{"Slot7", Name7},
{"Slot8", Name8},
{"Slot9", Name9}
};
foreach (KeyValuePair<string, string> nameSlot in nameSlots)
{
NextEmptyNameSlot = nameSlot.Key;
if (nameSlot.Value == "")
{
break;
}
}
Console.WriteLine($"Available name slot at {NextEmptyNameSlot}");
Console.WriteLine($"Content of empty name slot \"{nameSlots[NextEmptyNameSlot]}\"");
nameSlots[NextEmptyNameSlot] = fruitName;
Console.WriteLine($"Empty image slot has been assigned the value {nameSlots[NextEmptyNameSlot]}");
Console.WriteLine($"Empty image slot has been assigned the value {Name4}");
Console.ReadLine();
}
Sample output for AssignFruitToNextAvailableSlot("Strawberry") :
Available name slot at Slot4
Content of empty name slot ""
Empty image slot has been assigned the value Strawberry
Empty image slot has been assigned the value
As you can see the code works fine to identify the empty name slot, in this case Slot4. However when using the syntax...
nameSlots[NextEmptyNameSlot] = fruitName
... "strawberry" is assigned to nameSlots[NextEmptyNameSlot], but not the variable Name4. I tried using "ref" to assign by reference but that yielded various error messages.
**
What is the right syntax to assign the fruitName "Strawberry" to the Name4 string variable using the dictionary? Sorry if this is a very basic question. I am new to C#.
**

I think you are making this more complex than it needs to be. If you have a fixed number of elements and you want the order to remain the same then an array is the simplest way to handle the data.
Declare the array with 9 elements.
string[] fruitArray = new string[9];
Then keep a counter of how many slots you have used;
int NextSlot = 0;
Increment it after you add a fruit
NextSlot++;
Then you can iterate it to display the data
for(int loop =0; loop <= fruitArray.Length; loop++)
{
Console.WriteLine($"Slot {loop + 1} contains the value {fruitArray[loop]}");
}
If you don't have a fixed number of elements, or you don't know the size during design time then you can use a List<string> instead of an array.
A List will keep the insertion order of the data, until you sort it.

While it's not the primary problem here, a confusing factor is that strings are immutable, which means if you "change it" then it's thrown away and a new one is created. I'll come back to this point in a moment
The major problem here is that you can not establish a reference to something:
string s = "Hello";
Then establish another reference to it (like you're doing with dictionary)
string t = s;
Then "change the original thing" by changing this new reference out for something else:
t = "Good bye";
And hope that the original s has changed. s still points to a string saying "Hello". t used to point to "Hello" also (it never pointed to s pointed to hello in some sort of chain) but now points to a new string "Good bye". We never used the new keyword when we said "Good bye" but the compiler had to use it, and new made another object and changed the reference to it. Because references aren't chained, you cannot change what a downstream variable points to and hope that an upstream variable pointing to the same thing will also change.
//we had this
s ===> "Hello" <=== t
//not this
t ==> s ==> "Hello"
//then we had this
s ===> "Hello" t ===> "Good bye"
Because we have two independent reference that point to the same thing, the only way you can operate on one reference and have the other see it is by modifying the contents of what they point to, which means you will have to use something mutable, and not throw it away. This is where string confuses things because strings cannot be modified once made. They MUST be thrown away and a new one made. You don't see the new - the compiler does it for you.
So instead of using strings, we have to use something like a string but can have its contents altered:
StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = null;
var d = new Dictionary<string, StringBuilder>();
d["sb1"] = sb1;
d["sb2"] = sb2;
Now you can change your string in the mutable sb1, by accessing the stringbuilder via the dictionary:
d["sb1"].Clear();
d["sb1"].Append("Good bye");
Console.Write(sb1.Length); //prints 8, because it contains: Good bye
But you still cannot assign new references to your sb variables using the dictionary:
d["sb2"] = new StringBuilder("Oh");
//sb2 is still and will always be null
Console.Write(sb2.Length); //null reference exception
Using new here stops the dictionary pointing to sb2 and points it to something else, sb2 is not changed and is still null. There is no practical way to set sb2 to a stringbuilder instance, by using the dictionary d
This is also why the original string thing didn't work out - you can't change the content of a string - c# will throw the old string away and make a new one and every time new is used any reference that might have pointed to the old thing will now point to a new thing
As a result you'll have to init all your references to something filled or empty:
var sb1 = new StringBuilder("Hello");
var sb2 = new StringBuilder("Goodbye");
var sb3 = new StringBuilder("");
var sb4 = new StringBuilder("");
You'll have to link your dictionary to all of them:
d["sb1"] = sb1;
d["sb2"] = sb2;
d["sb3"] = sb3;
d["sb4"] = sb4;
And you'll have to skip through your dictionary looking for an empty one:
//find empty one
for(int i = 1, i <= 4; i++){
if(d["sb"+i].Length ==0)
return "sb"+i;
}
And then change its contents
This is all maaassively complex and I wholeheartedly agree with the answer given by jason that tells you to use arrays (because it's what they were invented for), but I hope i've answered your questions as to why C# didn't work the way you expected

If such strange thing is absolutely necessary, you can use Reflection.
var frutSlots = this.GetProperties()
.Where(p => p.Name.StartsWith("Name")).OrderBy(p => p.Name).ToList();
You'll get List of PropertyInfo objects, ordered by property name, through which you can iterate or just use Linq.
fruitSolts.First(fs => fs.GetValue(this).ToString()="").SetValue(this."somefruit");
But mind that reflections are not too quick and not too good for performance.

Related

How to use Split() for making a command system in c#

I want to do something like this:
string a = Console.Readline();
string[] b = a.Split(' ');
string i = b[0];
string j = b[1];
Now the problem is, putting the 'string j' may be optional like the input may be hi hello here hello is optional. How to make the code work if someone doesn't put something in place of hello.
Thanks in advance.
You could use the Length property to check how many elements are in the split array.
If there are not enough elements in the array to assign the optional value you can set it to null.
In the rest of your code you just have to null-check the optional value before using it.
Something liket his would work:
string input = Console.ReadLine();
string[] tokens = input.Split(' ');
string hi = tokens[0];
string optional = tokens.Length > 1 ? tokens[1] : null; // make sure we have enough elements
Console.WriteLine(hi);
// null check before using the optional value
if (optional != null)
Console.WriteLine(optional);
// or
Console.WriteLine(optional ?? "Optional value is null..");
Instead of accessing the arrays particular element by its index position, I would use foreach loop to iterate over a list like:
string a = Console.ReadLine();
string[] b = a.Split(' ');
foreach (string elem in b)
{
Console.WriteLine(elem); // Do whatever you want with each element
}
Console.ReadLine();
Since the "commands" entered by the user will be stored in the array (e.g. b based on your code) after the split, I don't think it's necessary to store them in individual variables yourself. Thus, avoiding the problem you have in your current setup. On the other hand, if you want to see if a specific "command" was keyed in, you can do something like this:
static void Main(string[] args)
{
Console.Write("> ");
string input = Console.ReadLine();
// Doing it like this will automatically remove blanks from the resulting array
// so you won't have to clean it up yourself
string[] commands = input.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
// Contains is from the System.Linq namespace
// this will allow you to see if a given value is in the array
if (commands.Contains("hi"))
{
Console.WriteLine("> The command 'hi' has been received.");
}
Console.Read();
}
You can use Linq's Contains method to check if a specific value exists in the array of command strings.
if you just want to see all the commands in the array, a simple for loop would be enough.
// The Length property of the array will give you the
// number of items it contains
for(int i = 0; i < commands.Length; i++)
{
Console.WriteLine("> Command read: {0} ", commands[i]);
}
One more thing, I suggest that you normalize the inputs your application will receive as to avoid problems when filtering through them. You could do this by calling the ToLower method available to ReadLine:
string inputs = Console.ReadLine().ToLower();
Happy coding :)

how to convert a String list into a String array then converting it into an int array then counting the total sum of the numbers in the array?

So I am so fresh into the world of programming, starting new, I decided to start messing around in C# to create simple apps from ideas that come to mind, with this little app, I'm trying to have multiple TextBoxes named d1,d2,d3,d4,etc... the user inserts numbers into the textboxes then clicks button1, which begins the process in the code below creating a new list which contains all of the values of the textboxes and then the list is converted to an array and the array is then converted into an int array, etc....
BUT, when starting the application and I add values to the textboxes and then click button1, it shows 2 error like shows in the //gray code line below
Please help.
private void button1_Click(object sender, EventArgs e)
{
List<string> dodo = new List<string>();
dodo.Add(d1.Text); dodo.Add(d2.Text); dodo.Add(d3.Text); dodo.Add(d4.Text); dodo.Add(d5.Text);
dodo.Add(d6.Text); dodo.Add(d7.Text); dodo.Add(d8.Text); dodo.Add(d9.Text); dodo.Add(d10.Text);
dodo.Add(d11.Text); dodo.Add(d12.Text); dodo.Add(d13.Text); dodo.Add(d14.Text); dodo.Add(d15.Text);
dodo.Add(d16.Text); dodo.Add(d17.Text); dodo.Add(d18.Text); dodo.Add(d19.Text); dodo.Add(d20.Text);
foreach(string numb in dodo)
{
if (numb == "")
numb = "0"; //numb word has a red underline
}
string[] terms = dodo.ToArray();
int[] valv = {};
int x = 0;
for(int i=0;i<=19;i++)
{
valv[i] = int.Parse(terms[i]); //the ; in the end has a red underline and shows "FormatException was unhandled" error
i++;
x = x + valv[i];
}
string myString;
myString = x.ToString();
Result1.Text = myString;
}
you can't change the iteration variable which is numb in your case. Please change in the List container instead
List<string> dodo = new List<string>();
dodo.Add(d1.Text); dodo.Add(d2.Text); dodo.Add(d3.Text); dodo.Add(d4.Text); dodo.Add(d5.Text);
dodo.Add(d6.Text); dodo.Add(d7.Text); dodo.Add(d8.Text); dodo.Add(d9.Text); dodo.Add(d10.Text);
dodo.Add(d11.Text); dodo.Add(d12.Text); dodo.Add(d13.Text); dodo.Add(d14.Text); dodo.Add(d15.Text);
dodo.Add(d16.Text); dodo.Add(d17.Text); dodo.Add(d18.Text); dodo.Add(d19.Text); dodo.Add(d20.Text);
int k = 0;
foreach (string numb in dodo)
{
if (numb == "")
{
//numb = "0"; //numb word has a red underline
dodo[k] = "0";
}
k++;
}
Now your code on parsing into integer won't give any runtime error.
The first line "tells" you that you are not able to assign a new value to the variable which is used as a foreach iteration variable.
The second line, "tells" you that you have string value which is not able to be parsed correctly (e.g. user put string which is not a number). To avoid this you can use Int32.TryParse method instead, which will safely try to parse the given string.
The best and easiest way to achieve what you need is using LINQ methods, here is the example based on few things/assumptions:
Since you are converting empty strings into zeros, you could simply skip those entries from counting
To avoid FormatException, you should use TryParse method instead. Since TryParse method will safely parse the given string, you don't even have to filter empty strings at all (they will be skipped). However, I deliberately left filtering part, to get you a better overview of a solution.
You can use list initializer to make list initialization more readable
Solution:
List<string> dodo = new List<string>()
{
d1.Text, d2.Text //...others
};
int sum = dodo
.Where(item => !String.IsNullOrEmpty(item))
.Sum(item =>
{
if (Int32.TryParse(item, out int parsedItem))
{
return parsedItem;
}
return 0;
});
You can get more familiar with LINQ and used methods on following link

Do synonyms exist?

Is there a way to have a variable set to an object and have another variable which is always equals to the former one?
var x = new object();
var y = [synonym of x];
x = null; // then y = null as well
I don't think this exists.
So I've often used arrays to hold "references".
var x = new object[] { new object() };
var y = x;
x[0] = null; // then y[0] = null as well
But it feels kinda lame.
If you really really need to this you can do something like below but I think it is still lame (:
class RefHolder<T>
{
public RefHolder(T value)
{
Value = value;
}
public T Value { get; set; }
}
Usage
var o1 = new RefHolder<object>(new object());
var o2 = o1;
o2.Value = null; // now o1.Value is null too
You can do it but the price you have to pay is to use undocumented keywords/features. They're there from long time ago and probably they won't change or disappear but...
It'll make your code more complicated to read (it may be useful if supported by the language itself) but it's bidirectional and you can move around your original object, changes will always be reflected to your "reference" too. It differs from Mehmet Ataş answer because you can pass the original object to another function and changes will propagate to your synonym too. They have limitations (they can't be used for class fields) but they works for parameters and local variables.
What you need is a TypedReference, it holds a reference to another variable then if you assign a new value to it you'll change the original variable. This in theory could open the door to synonyms if someday they'll think it's a good feature to include.
Let's see an example:
var text = "initial value";
var synonym = __makeref(text);
Now synonym is a reference to text (please note it's a reference to text and not to the value it holds). To get original value from a TypedReference you use __refvalue like this:
Console.WriteLine(__refvalue(synonym, string));
They have the same value:
Debug.Assert(__refvalue(synonym, string) == text);
Now let's change text to a new value:
text = "second value";
Debug.Assert(__refvalue(synonym, string) == text);
And even opposite is true:
__refvalue(synonym, string) = "third value"; // <---
Debug.Assert(__refvalue(synonym, string) == text);
Finally let's modify the original variable within another function (unaware of the reference it'll see a normal variable):
void ChangeIt(ref string value) { value = "another value"; }
ChangeIt(ref text);
Debug.Assert(__refvalue(synonym, string) == text);
All of this works will value types as well. Note that this creates a synonym for a variable, not an alias (you can imagine them as a safe pointer - to pointer in case of reference type). Let's try this:
void foo1()
{
string text = "ABC";
foo2(text);
// text holds the original value "ABC"
// not the value modified in foo2
}
void foo2(string value)
{
value = "123";
var synonym = __makeref(value);
__refvalue(value, string) = "456";
// both value and synonym holds "456";
}
Well, you are basicly describing a C++ reference (or a C pointer).
This can be done in C#, too, but you REALLY do not want to do this unless you absolutely need to.
unsafe static void Main(string[] args)
{
int a = 5;
int *b = &a;
*b = 0;
Console.WriteLine(a);
}
This will output 0 to the console.
You can read more about unsafe code in Unsafe Code and Pointers article on MSDN.
It depends. .NET contains both Reference and Value types. Value types are all the basic types, int, bool etc.. plus string. Reference types are everything else, including anything you create for yourself.
So, for example, value types...
int a = 3;
int b = a;
b = 5;
// a is still 3
While with references
class Mine {
public int A { get; set; }
}
Mine A = new Mine() { A = 3; }
Mine B = A;
B.A = 5;
// A.A is now 5.
you can asign like
var parentObject={};
parentobject['child1']="test1";
parentobject['child2']="test2";
parentobject['child3']="test3";
after
console.log(parentObject);
you get following output
object{child1="test1",child2="test2",child2="test2"}

C# - Compile variable as name/code

Since I couldn't explain very good in my last question and I didn't get an answer that could satisfy me, I decided to open a new one. Straight to the point, what I'm basically trying to do is compiling a variable (the value it holds) as a part of code (and specificly in my case referencing another variable)
Say I have:
int var_1, var_2, var_3 ... var_10;
for (int i = 1; i <= 10; i++)
{
var_%i%=20; //if i is 1, then var_1's value will be set to 20, if i is 2, then var_2's value will be set to 20. So basically this loop sets the value of var_1 .. var_10 to 20
}
I can explain in an even simpler way, if in any case the latter is not clear.
int var_5;
int SomeOtherVar = 5;
var_%SomeOtherVar% = 10; // so var_5 (where 5 is the value of SomeOtherVar) is set to 10
Is this doable and if it is, what's the approach?
No you can't do that, why dont you use an array?
int[] array = new int[3];
for (int i = 0; i < array.Length; ++i)
{
array[i] = 20;
}
Hope it helps.
It's not doable. Use an array instead. The type is int[] but I suggest you go read a tutorial about arrays to understand how to create and use them.
I can't think of a situation where you'd need to do this. If you wish to store values against a consecutive list of numbers, use an array. Otherwise you could use a Dictionary. For example to store "var1" = 20, "var2" = 20 as in your question, you could do this:
Dictionary<string, int> dict = new Dictionary<string, int>();
for (int i = 1; i <= 10; i++)
{
dict.Add("var" + i.ToString(), 20);
}
Some examples of usage:
dict["var1"] // 20
dict["var2"] // 20
dict.ContainsKey("var3") // true
dict.ContainsKey("var99") // false
Note: I'm concatenating the string "var" with the int from the loop just to demonstrate that you can use arbitary strings as keys to store / lookup the values. In this case it's a bit of a strange thing to do, and you'd probably be best sticking to a normal array, but my example shows how a dictionary could work with more complex keys.
If you want to bypass static type checking and you feel like creating some coding horror, you can use ExpandoObject combined with the dynamic keyword. Won't let you set variables in your scope, but will technically let you declare your own ones. Note that in my example I cast it to IDictionary<string, object> because I create its members' names at runtime from a string. What the following method does is create twenty members and assign their values from 0 to 19.
static dynamic SetVariables(IEnumerable<int> range)
{
const string variableName = "var_";
var expandoDictionary = new ExpandoObject() as IDictionary<string, object>;
foreach (var i in range)
expandoDictionary[variableName + i] = i;
return expandoDictionary;
}
You can then access the members easily this way:
var container = SetVariables(Enumerable.Range(0, 20));
var value13 = container.var_13;
Please note that I do not recommend this usage, and I'd stay away from dynamic as much as I can. However, for the sake of problem solving, this can be seen as one unsafe but partial solution.

C# array of variables

Is it possible to do something like this:
string A = "A";
string B = "B";
object[] O = { A, B };
O[0] = "C";
Where A will hold the value "C" in the end?
The code above will replace O[0] with "C", but A remains unchanged.
No - at least not in safe code.
When you create the array, it copies the values into the array. Changing the value in the array later won't change the value of the variable. There's no way of creating a sort of "ref array", where the array elements are shared with the variables. (As shown in Mark's answer, there are sometimes ways of doing this in unsafe code, but I'd really suggest staying away from that.)
Note, however, that if both the array and the variable refer to the same mutable object then mutating that object via either path will make a change which is visible via the other:
StringBuilder x = new StringBuilder();
StringBuilder[] array = new StringBuilder[] { x };
array[0].Append("Foo");
Console.WriteLine(x); // Prints "Foo"
This is no different to any other assignment though - and note that in the code above, the Append call doesn't change the value of either x or the array element; it changes the data within the object that those values refer to.
While the answer is no in the case of string, you can do this with value types:
class Program
{
static unsafe void Main()
{
char A = 'A';
char B = 'B';
var O = new char*[] { &A, &B };
*O[0] = 'C';
System.Console.WriteLine(A + "," + B); // outputs C,B
}
}
Unsafe code is typically frowned upon in C#. So, while this kind of thing is possible, I wouldn't recommend doing it.
What you probably want is to convert your strings into objects with properties.
You can then Cast your array object on the specific key to this object type you created, and then you can set its property again.
This way you can change both what's in your array, as your original variable, therefor, it is similar to seeing it as an array with values by reference.
public class stringObject
{
private string name;
public string Name { get { return name; } set { name = value; } }
}
stringObject A = new stringObject();
A.Name = "A";
stringObject B = new stringObject();
B.Name = "B";
object[] O = { A, B };
//change the array at key 0, and also changing our original variable
stringObject C = O[0] as stringObject;
C.Name = "C";
The above code will not only change what is inside O[0], but it will also update what is inside your object A.
An example with a test to write to the console can be seen here:
https://dotnetfiddle.net/Yt25hy

Categories

Resources