C# getting first digit of int in custom class - c#

I am trying to build a help function in my guess the number game, whereby the user gets the first digit of the number he/she has to guess. So if the generated number is 550, he will get the 5.
I have tried a lot of things, maybe one of you has an idea what is wrong?
public partial class Class3
{
public Class3()
{
double test = Convert.ToDouble(globalVariableNumber.number);
while (test > 10)
{
double firstDigit = test / 10;
test = Math.Round(test);
globalVariableNumber.helpMe = Convert.ToString(firstDigit);
}
}
}
Under the helpButton clicked I have:
private void helpButton_Click(object sender, EventArgs e)
{
label3.Text = globalVariableNumber.helpMe;
label3.AutoSize = true;
That is my latest try, I putted all of this in a custom class. In the main I putted the code to show what is in the helpMe string.
If you need more code please tell me

Why not ToString the number and use Substring to get the first character?
var number = 550;
var result = number.ToString().Substring(0, 1);
If for some reason you dont want to use string manipulation you could do this mathematically like this
var number = 550;
var result = Math.Floor(number / Math.Pow(10, Math.Floor(Math.Log10(number))));

What's wrong - you have an infinite while loop there. Math.Round(test) will leave the value of test unchanged after the first iteration.
You may have intended to use firstDigit as the variable controlling the loop.
Anyway, as suggested by others, you can set helpMe to the first digit by converting to a string and using the first character.
As an aside, you should consider supplying the number as a parameter and returning the helpMe string from the method. Your current approach is a little brittle.

The problem with your code is that you are doing the division and storing that in a separate variable, then you round the original value. That means that the original value only changes in the first iteration of the loop (and is only rounded, not divided), and unless that happens to make the loop condition false (i.e. for values between 10 and 10.5), the loop will never end.
Changes:
Use an int intead of a double, that gets you away from a whole bunch of potential precision problems.
Use the >= operator rather than >. If you get the value 10 then you want the loop to go on for another iteration to get a single digit.
You would use Math.Floor instead of Math.Round as you don't want the first digit to be rounded up, i.e. getting the first digit for 460 as 5. However, if you are using an integer then the division truncates the result, so there is no need to do any rounding at all.
Divide the value and store it back into the same variable.
Use the value after the loop, there is no point in updating it while you still have multiple digits in the variable.
Code:
int test = (int)globalVariableNumber.number;
while (test >= 10) {
test = test / 10;
}
globalVariableNumber.helpMe = test.ToString();

By using Math.Round(), in your example, you're rounding 5.5 to 6 (it's the even integer per the documentation). Use Math.Floor instead, this will drop the decimal point but give you the number you're expecting for this test.
i.e.
double test = Convert.ToDouble(globalVariableNumber.number);
while (test > 10)
{
test = Math.Floor(test / 10);
globalVariableNumber.helpMe = Convert.ToString(firstDigit);
}
Like #Sam Greenhalgh mentions, though, returning the first character of the number as a string will be cleaner, quicker and easier.
globalVariableNumber.helpMe = test >= 10
? test.ToString().SubString(0, 1)
: "Hint not possible, number is less than ten"
This assumes that helpMe is a string.
Per our discussion in the comments, you'd be better off doing it like this:
private void helpButton_Click(object sender, EventArgs e)
{
label3.Text = GetHelpText();
label3.AutoSize = true;
}
// Always good practice to name a method that returns something Get...
// Also good practice to give it a descriptive name.
private string GetHelpText()
{
return test >= 10 // The ?: operator just means if the first part is true...
? test.ToString().SubString(0, 1) // use this, otherwise...
: "Hint not possible, number is less than ten" // use this.
}

Related

What is stopping this program from having an output

I made this code to brute force anagrams by printing all possible permutables of the characters in a string, however there is no output. I do not need simplifications as I am a student still learning the basics, I just need help getting it to work this way so I can better understand it.
using System;
using System.Collections.Generic;
namespace anagramSolver
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter anagram:");
string anagram = Console.ReadLine();
string temp = "";
List<string> storeStr = new List<string>(0);
List<int> storeInt = new List<int>(0);
//creates factorial number for anagram
int factorial = 1;
for (int i = anagram.Length; i > 0; i--)
{
factorial *= i;
}
while (storeStr.Count != factorial)
{
Random rnd = new Random();
while(temp.Length != anagram.Length)
{
int num = rnd.Next(anagram.Length - 1);
if (storeInt.Contains(num) == true)
{
}
else
{
storeInt.Add(num);
temp += anagram[num];
}
}
if (storeStr.Contains(temp) == true)
{
temp = "";
}
else
{
storeStr.Add(temp);
Console.WriteLine(temp, storeStr.Count);
temp = "";
}
}
}
}
}
edit: added temp reset after it is deemed contained by storeStr
Two main issues causing infinite loop:
1)
As per Random.Next documentation, the parameter is the "exclusive" upper bound. This means, if you want a random number between 0 and anagram.Length - 1 included, you should use rnd.Next(anagram.Length);.
With rnd.Next(anagram.Length - 1), you'll never hit anagram.Length - 1.
2)
Even if you solve 1, only the first main iteration goes well.
storeInt is never reset. Meaning, after the first main iteration, it will have already all the numbers in it.
So, during the second iteration, you will always hit the case if (storeInt.Contains(num) == true), which does nothing and the inner loop will go on forever.
Several issues here...
The expression rnd.Next(anagram.Length - 1) generates a value between 0 and anagram.Length - 2. For an input with two characters it will always return 0, so you'll never actually generate a full string. Remove the - 1 from it and you'll be fine.
Next, you're using a list to keep track of the character indices you've used already, but you never clear the list. You'll get one output (eventually, when the random number generator covers all the values) and then enter an infinite loop on the next generation pass. Clear storeInt after the generation loop to fix this.
While not a true infinite loop, creating a new instance of the random number generator each time will give you a lot of duplication. new Random() uses the current time as a seed value and you could potentially get through a ton of loops with each seed, generating exactly the same values until the time changes enough to change your random sequence. Create the random number generator once before you start your main loop.
And finally, your code doesn't handle repeated input letters. If the input is "aa" then there is only a single distinct output, but your code won't stop until it gets two. If the input was "aab" there are three distinct permutations ("aab", "aba", "baa") which is half of the expected results. You'll never reach your exit condition in this case. You could do it by keeping track of the indices you've used instead of the generated strings, it just makes it a bit more complex.
There are a few ways to generate permutations that are less error-prone. In general you should try to avoid "keep generating random garbage until I find the result I'm looking for" solutions. Think about how you personally would go about writing down the full list of permutations for 3, 4 or 5 inputs. What steps would you take? Think about how that would work in a computer program.
this loop
while(temp.Length != anagram.Length)
{
int num = rnd.Next(anagram.Length - 1);
if (storeInt.Contains(num) == true)
{
}
else
{
storeInt.Add(num);
temp += anagram[num];
}
}
gets stuck.
once the number is in storeInt you never change storeStr, yet thats what you are testing for loop exit

using a textbox to input the data in the scores textbox

Here is the right example
Need to use int data types.
SO, I am able to enter a number and but I get duplicate of a just one number.
Here is what I get on my code:
//create a string for the name
string name = txtName.Text;
// create a int for the number
// have those now have to get three amounts
// for each name then store that
// make a size three array
//innt to put the array in the for loop
int[] x = new int[3];
for (int i = 0; i < x.Length; i++)
{
//txtScore.Text
//txtStoreScores.Text
int covert = Int32.Parse(txtScore.Text);
x[i] = covert;
txtStoreScores.AppendText(x[i].ToString());
// ok I just need one number
// and split them by each number
}
Honestly, though you have selected an answer this feedback may be beneficial to future solutions. Your button once triggered should fire an event, inside that could you want to ensure you properly sanitize user input. Assume the user will cause an error, because the amount of variables they can input are infinite.
protected void btnAddScore_Click(object sender, EventArgs e)
{
if(float.TryParse(txtGrade.Text, out grade) && grade >= 0)
txtScores.Text += $"{grade}, ";
}
That simple conditional will sanitize, by ensuring that the user has entered a valid numeric and the score is higher than zero. Especially since a negative number would be unlikely. The primary point, your code should reflect the usage. Your code should account for those anomalies and variances that may occur.
Another nifty notion, you are outputting directly based on input. When you save that information, you would need to parse the data. So realize that you may need to something along these lines:
var scores = txtScores.Text.Split(','); // An array.
txtScores.Text = String.Join(",", scores); // Converts array into single string
The problem will increase in complexity over time, so be aware of that. Hopefully this was helpful. As for your math, if you do the following it would calculate:
var sum = txtScores.Text.Split(", ").Sum(grade => (float)grade);
The above makes a potentially flawed assumption, that all would cast to a float without an issue. These are items you need to think about. If you do not cast then your sum would be reflected by the length of your string not the values.
if i understand this right!!! you want to append the number from txtScore to txtStoreScores each time you click Add Scores btn!!!!
in this case you don't need a loop, simply put this in Add Scores btn event handler
txtStoreScores.Text += txtScore.Text + " ";
you reaaaaaly need to give more details and a clear question. Good luck!

Comparing a text box's input to a variable

My program requires users to input answers into a text box to randomly generated questions. The questions have calculated answers which are stored in variables. Comparing the text box's input with that variable doesn't work properly, hence why I'm here.
enum elements { lithium, beryllium, sodium, magnesium };
public void Moles()
{
string elementName;
int elementsLength = Enum.GetNames(typeof(elements)).Length;
double moles, mass, roundedMoles, Mr = 0;
Random random = new Random();
elementName = (Enum.GetNames(typeof(elements))[random.Next(0, elementsLength)]);
mass = random.Next(1, 100);
switch (elementName)
{
case "lithium":
Mr = 7;
break;
case "beryllium":
Mr = 9;
break;
case "sodium":
Mr = 23;
break;
case "magnesium":
Mr = 24;
break;
}
moles = mass / Mr;
roundedMoles = Math.Round(moles);
label1.Text = ("How many moles in " + mass + "g" + " of " + elementName + "?");
string input = textBox1.Text.ToString();
if (input == roundedMoles.ToString())
{
MessageBox.Show("Good");
textBox1.Clear();
}
else
{
MessageBox.Show("Bad");
textBox1.Clear();
}
private void button1_Click(object sender, EventArgs e)
{
Moles();
}
As you can see, the program will calculate a number of moles and round it to the nearest whole number. It's then stored in a variable, 'roundedMoles' - this variable is what's compared with the text box's input.
However, the message box will randomly show 'Good' or 'Bad' regardless of whether the input is correct or not. Bear in mind that this code works fine in a console application, so I don't know whether it's something I'm doing wrong or if it just doesn't want to work.
Sorry if this code isn't of a high standard. I'm not that good at programming.
You have two problems.
Don't compare strings as numbers
When comparing numeric values, never compare string to string. Always convert to a numeric variable. Otherwise, "0.00" will not equal "0" and "1234 " (with a space at the end) won't equal "1234" (with no space). Also, you will run into serious trouble if you're working with a culture that uses , as the decimal point.
So, instead of this:
string input = textBox1.Text.ToString();
if (input == roundedMoles.ToString())
{
MessageBox.Show("Good");
textBox1.Clear();
Use something like this:
string input = textBox1.Text;
double inputAsDouble;
bool ok = double.tryParse(out inputAsDouble);
if (!ok)
{
MessageBox.Show("Please enter a numeric value.");
return;
}
if (inputAsDouble == roundedMoles)
{
MessageBox.Show("Good");
textBox1.Clear();
Don't compare floating point values as exact
When comparing floating point values, don't use ==. Instead, compute the difference and make sure it is below a certain tolerance. You have to do this because floating point variables are only approximations.
So instead of
if (inputAsDouble == roundedMoles)
use
const double Tolerance = 0.00001;
if (inputAsDouble - roundedMoles < Tolerance)
Ok so here is the answer...
The reason the console works is that you write to the console to ask the question. You then do console.Readline() this captures the input of the answer after they have seen the question.
On the forms version. You set the label to the question and immediately capture the text box value. The user has not had a chance to enter the answer, or its still the answer from the last time (hence why it randomly goes from "Good" to "Bad" because sometimes the answer is the same).
One simple way is make your variables at the top of the Moles() method, class variables on the form and then have 2 buttons with 2 methods one that gets the questions and sets your answer variables and then a second button then the user can press when they've entered their answer.
Check out this code paste https://www.pastebucket.com/564406
(You'll need button2 added to your form for this to work obviously)
edit: I've done a quick cut and paste of code there. You shouldn't just copy this and use it. Look what I've done and apply it properly and try and understand why it works
It seems that you might have a culture related problem down there. Always be careful when comparing Double values converted to String. The framework might be converting '1,5' to '1.5' depending on your regional settings.
The first thing I would recomend you is not to compare as String but do it as Double. And if you do that, ensure that you will replace dots with comma or other way.
if (Convert.ToDouble(input) == roundedMoles)
{
MessageBox.Show("Good");
textBox1.Clear();
}
else
{
MessageBox.Show("Bad");
textBox1.Clear();
}

Is there a fast extraction of specific value located inside tags in a slice from a list of strings possible?

I'm using a 2 step regex to extract the value of the first occurance of a specific marker inside a list of strings:
Regex regexComplete = new Regex(
#"MyNumberMarker"
+ #"[\d]+"
+ #"[\s]+Endmarker"
);
Regex regexOnlyNumber = new Regex(
#"MyNumberMarker"
+ #"[\d]+"
);
int indexmyNumber = eintraegeListe.FindIndex(
5,
10000,
x => regexComplete.IsMatch(x)
);
if (indexmyNumber >= 0)
{
int myNumber = 0;
string myNumberString = regexOnlyNumber.Match(regexComplete.Match(eintraegeListe[indexmyNumber]).Value).Value;
myNumberString = myNumberString.Replace("MyNumberMarker", "").Replace("\n", "").Replace("\r", "").Trim();
if (Int32.TryParse(myNumberString, out myNumber))
{
return myNumber;
}
}
As one can see the value I really want is located between "MyNumberMarker" and "Endmarker". It is in a specific part of the list which I search through with the findIndex command. Then I use regex to extract the complete value + tag and reduce it to "just" the begin tag and the value and then manually cut away the begin tag and all could be white spaces (including \n and \r).
Now this works quite fine as intended but if I do this a couple of thousand times it is quite slow in the end. Thus my question.
Is there any better (faster) way to do this?
As a note: eintraegeListe can have between 100 and 30000 entries.
For example if I have the following small list:
[0]This is a test
[1]22.09.2015 01:00:00
[2]Until 22.09.2015 03:00:00
[3]................................
[4]................................
[5]........ TESTDATA
[6]...............................
[7]................................
[8]MyNumberMarker519 Endmarker
[9]This is a small
[10]Slice of Test data with
[11]520 - 1 as data.
I would expect 519 to be returned.
Since you are returning a single item, the performance of code past FindIndex is irrelevant: it is executed only once, and it takes a single string, so it should complete in microseconds on any modern hardware.
The code that takes the bulk of CPU is in x => regexComplete.IsMatch(x) call. You can tell that this code is returning false most of the time, because the loop is over the first time it returns true.
This means that you should be optimizing for the negative case, i.e. returning false as soon as you can. One way to achieve this would be to look for "MyNumberMarker" before employing regex. If there is no marker, return false right away; otherwise, fall back on using the regex, and start from the position where you found the marker:
int indexmyNumber = eintraegeListe.FindIndex(
5,
10000,
x => {
// Scan the string for the marker in non-regex mode
int pos = x.IndexOf("MyNumberMarker", StringComparison.Ordinal);
// If the marker is not there, do not bother with regex, and return false
return pos < 0
? false
// Only if the marker is there, check the match with regex.
: regexComplete.IsMatch(x, pos);
}
);
You can actually merge the two regexps into 1 containing a capturing group that will let you access the sequence of digits directly via the group name (here, "number").
Regex regexComplete = new Regex(#"MyNumberMarker(?<number>\d+)\s+Endmarker");
Now, you do not need regexOnlyNumber.
Then, you can add a non-regex condition as in the other answer. Maybe this will be enough (the .Contains will be executed first and the whole expression should evaluate to false if the first condition is not met - see "short-circuit" evaluation) (IndexOf with StringComparison.Ordinal looks preferable anyway):
int indexmyNumber = eintraegeListe.FindIndex(5, 10000, x => x.Contains("MyNumberMarker") && regexComplete.IsMatch(x));
And then:
if (indexmyNumber >= 0)
{
int myNumber = 0;
string myNumberString = regexComplete.Match(eintraegeListe[indexmyNumber]).Groups["number"].Value;
if (Int32.TryParse(myNumberString, out myNumber))
{
return myNumber;
}
}

Check how many instances of strings in a list

Well I enquired about checking if certain keywords can be found in an list and if they are all there the question is correct. Found here: Check if the string contains all inputs on the list
What I would like to also know is how many of the words are in the list, then divide it and get a percentage, so the user knows how accurately they answered each question.
public String KeyWords_Found()
{
int Return_Value = 0;
foreach (String s in KeyWords)
{
if (textBox1.Text.Contains(s))
{
Return_Value++;
}
}
int Holder = Return_Value / KeyWords.Count;
int Fixed = Holder * 100;
return Fixed + "%";
}
So what I want that code it do is check for all instances of keywords listed into the list KeyWords. Then get the percentage by dividing by the total amount of keywords and multiplying by 100. But it says that both values are 0 and i cant divide by 0. I'm not sure why they would be zero. Confused! Help!
You should first check, if KeyWords is empty or not
public String KeyWords_Found()
{
if (KeyWords.Count == 0)
return "0%";
// rest of the code
}
Alternatively you could use Linq instead of writing your own method:
int nOfOccurences = KeyWords.Where(k => textBox1.Text.Contains(k)).Count();
make sure you are using System.Linq; for that to work.
You'll still need to check for KeyWords.Count == 0 and compute the percentage yourself, though.
You should use floating point maths instead of integer maths in your calculations.
int i=100;
int a=51;
(i/a)==0 //true, integer division sucks for calculating percentages
((double)i/a)==0 //false, actually equals ~1.96

Categories

Resources