Algorithm confusion regarding the number conversion in C# - c#

I am following a tutorial regarding converting an integer number into a spoken-word equivalent in C#.
I am getting a bit confused about the three digit rule.
// Single-digit and small number names
private static string[] smallNumbers = new string[]
{
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen"
};
// Tens number names from twenty upwards
private static string[] tens = new string[]
{
"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
"eighty", "ninety"
};
// Scale number names for use during recombination
private static string[] scaleNumbers = new string[]
{
"", "thousand", "million", "billion", "trillion",
"quadrillion", "quintillion"
};
public static string ToWords(this BigInteger number)
{
if (number == 0)
{
return "zero";
}
int[] digitGroups = new int[groups];
// Ensure a positive number to extract from
var positive = BigInteger.Abs(number);
// Extract the three-digit groups
for (int i = 0; i < groups; i++)
{
digitGroups[i] = (int)(positive % 1000);
positive /= 1000;
}
//The rest of the code..
}
I am assuming now we are converting a number and its value is 25111.
In the for-loop function, the return value of (int)(positive % 1000) should be 111. The 111 does not match any elements in digitalGroups array.
I don't quite get it, can someone explain it to me? Thanks in advance.

The code you are showing us is not matching but rather assigning the value 111 to the first item of the digitGroupsArray.
How many items has digitGroupsArray? I don't know, it depends on the 'groups' variable value, which we can't see in the code excerpt.
Here:
int[] digitGroups = new int[groups];
you're creating a new empty integer array called digitGroups with the length of (int) 'group' value.
While this,
for (int i = 0; i < groups; i++)
{
digitGroups[i] = (int)(positive % 1000);
positive /= 1000;
}
is a cylce, an iteration. And note that each time the 'positive' variable gets divided by 1000 (like positive = positive / 1000).
The result will be like this:
digitGroups[0] = (int)(25111 % 1000) // first item of digitGroupsarray
'positive' gets divided (25111 / 1000) next time will be 25
digitGroups[1] = (int)(25 % 1000) // second item of digitGroupsarray
'positive' gets divided again (25 / 1000) next time will be 0
and so on...
In these situation is very common and useful to log the values and debug the cycle. It really clears up your mind.
Working example:
static void Main(string[] args)
{
int groups = 3;
int[] digitGroups = new int[groups];
int positive = 25111;
for (int x = 0; x < groups; x++)
{
Console.WriteLine("positive value is: " + positive);
digitGroups[x] = (int)(positive % 1000);
positive /= 1000;
Console.WriteLine("item number (index): " + x + " value: " + digitGroups[x]);
}
}
Outputs:
positive value is: 25111
item number (index): 0 value: 111
positive value is: 25
item number (index): 1 value: 25
positive value is: 0
item number (index): 2 value: 0

Related

Numbers to rounded off text

I am using the following function to convert numbers to text:
public static string NumberToText(long number)
{
StringBuilder wordNumber = new StringBuilder();
string[] powers = new string[] { "Thousand ", "Million ", "Billion " };
string[] tens = new string[] { "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" };
string[] ones = new string[] { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten",
"Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" };
if (number == 0) { return "Zero"; }
if (number < 0)
{
wordNumber.Append("Negative ");
number = -number;
}
long[] groupedNumber = new long[] { 0, 0, 0, 0 };
int groupIndex = 0;
while (number > 0)
{
groupedNumber[groupIndex++] = number % 1000;
number /= 1000;
}
for (int i = 3; i >= 0; i--)
{
long group = groupedNumber[i];
if (group >= 100)
{
wordNumber.Append(ones[group / 100 - 1] + " Hundred ");
group %= 100;
if (group == 0 && i > 0)
wordNumber.Append(powers[i - 1]);
}
if (group >= 20)
{
if ((group % 10) != 0)
wordNumber.Append(tens[group / 10 - 2] + " " + ones[group % 10 - 1] + " ");
else
wordNumber.Append(tens[group / 10 - 2] + " ");
}
else if (group > 0)
wordNumber.Append(ones[group - 1] + " ");
if (group != 0 && i > 0)
wordNumber.Append(powers[i - 1]);
}
return wordNumber.ToString().Trim();
}
This works fine.
The issue is that the returned text is too long to read. I want to convert it to something shorter.
For eg: 345435234 is returned as three hundred and forty-five million, four hundred and thirty-five thousand, two hundred and thirty-four.
I instead would like 345.4 Mil.
Here are some more examples:
3454 should be 3.4K
34543 should be 34.5K
345433 should be 345.4K
... and so on.
How do I achieve this?
Maybe something like this?
Though if you have "printable character OCD" you could probably get it smaller
private static string[] suffixes = new[] { "", "K", "Mill", "Bill", "Trill", "Zill" };
public static string ToStuff(double number, int precision = 2)
{
const double unit = 1000;
var i = 0;
while (number > unit)
{
number /= unit;
i++;
}
if(i >= 5) throw new Exception("No one can count this high");
return Math.Round(number, precision, MidpointRounding.AwayFromZero) + suffixes[i];
}
Disclaimer : Totally untested
Update
Due to popular demand, i tested it with a test case of 1 E.g 2500000, 0 and it output 2Mill. I think i might have a career in this counting game
Update 2
Due to more popular demand, the consensus is that we shouldn't use .Net default rounding ToEven (bankers rounding) , and should use AwayFromZero
Try the following:
if (number>1000000)
string = floor(number/1000000).ToString() + "m";
else if (number > 1000)
string = floor(number/1000).ToString() + "k";
else
string = number.ToString();

How to get each number between words in string as separate value to replace

First I've to say that's I'm not asking, how to convert integer value to the string phrase.
My question is how to get each number exist between words in string as separate value to replace 46 000 000 with forty-six million and 70 000 000 with seventy million. To make it easier and not confuse for example if string value already exists:
string val1 = "replace1";
string val2 = "replace2";
and input string:
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
to get this result:
distance from the Sun ranging from replace1 to replace2 km
I'm not asking how to replace value to other value, my question is how to extract exist numbers from string as separate values to replace it after.
string foundNum1 = "46 000 000";
string foundNum2 = "70 000 000";
Only then I can use replace method:
string f1 = inputStr.Replace(foundNum1, val1);
string outStr = f1.Replace(foundNum2, val2);
Here is a complete answer for any number you need to convert:
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
public string ConvertStringNumbersToWrittenForm(string inputStr)
{
var regex = new Regex(#"[\d][\d ]+[\d]");
var matches = regex.Matches(inputStr);
string newString = inputStr;
foreach (var match in matches)
{
string matchStr = match.ToString();
//eliminate spaces
matchStr = matchStr.Replace(" ", string.Empty);
newString = newString.Replace(match.ToString(), NumberToWords(Convert.ToInt32(matchStr)));
}
return newString;
}
To use it, you call:
string wordString = ConvertStringNumbersToWrittenForm("distance from the Sun ranging from 46 000 000 to 70 000 000 km");
and wordString will be the resulting string, with written words for the numbers.
To do this dynamically, you could use a regex like follows:
string val1 = "replace1";
string val2 = "replace2";
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
var regex = new Regex(#"[\d][\d ]+[\d]");
var matches = regex.Matches(inputStr);
inputStr.Replace(matches[0], val1);
inputStr.Replace(matches[1], val2);
Just note the following:
if there are not two matches, this won't wokr
if the matches are the same, this will always replace both
there are probably other edge cases
This will get you back any group of numbers.
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
Regex pattern = new Regex(#"([0-9]{1,3}(?:\s[0-9]{3})*)");
foreach (Match match in pattern.Matches(inputStr))
{
Console.WriteLine(match.Groups[1]);
}
This displays each match. Now you would just need to do your replace the way you want. Not sure what you are planning there; that's why I didn't put anything in place.
You can do something like this:
string res = inputStr.Replace("46 000 000", val1).Replace("70 000 000", val2);
I'm pretty sure you do it like this:
inputStr = inputStr.Replace("46 000 000", "forty six million");
inputStr = inputStr.Replace("70 000 000 ", "seventy million");

Efficient way to count the number of appearances of a digit in a number

I need to count the number of times a single digit (not 0) appears in a number (positive integer) of varying length.
The obvious solution is to convert the number to a string, the digit to a character and iterate over the string to count the number of times the character appears in the string.
static int CountDigitInString(string searchString, char digit)
{
int sum = 0;
for (int i = 0; i < searchString.Length; i++)
{
if (searchString[i] == digit)
sum++;
}
return sum;
}
The problem with this method, however, is that it is too slow for my purposes as I am running it many times.
public static void Run()
{
for (int i = 0; i < 1000000; i++)
{
CountDigitInString(i.ToString(), (char)j);
}
}
After I noted that the process took too much time, the CPU sampling profiler showed me that the problem was with the conversion to string.
So, how do I efficiently count the number of times a digit (single digit only, not a number) appears in a number (of any length)?
Here is more optimized version of #shaitibber solution. It replaces one division with multiplying and returns 1 for 0,0. It is about 20% faster.
static int CountDigitsInString2(int number, int digit)
{
int sum = 0;
do
{
int n2 = number / 10;
if (number - n2 * 10 == digit)
sum++;
number = n2;
} while (number != 0);
return sum;
}
And here is solution about three times faster than that (but does not work for 0 digit, which is not required). It precalculates results for numbers 0..9999.
private static int[][] cache = new int[10][];
private const int cacheSize = 10000;//or 100000
private static int[] initCache(int digit)
{
var ca = cache[digit] = new int[cacheSize];
for (int i = 0; i < ca.Length; ++i)
{
ca[i] = CountDigitsInString2(i, digit);
}
return ca;
}
static int CountDigitsInString3(int number, int digit)
{
var ca = cache[digit] ?? initCache(digit);
int sum = 0;
while (number != 0)
{
int n2 = number / cacheSize;
sum += ca[number - n2 * cacheSize];
number = n2;
};
return sum;
}
I found a way which turned out to be about 3 times as fast on average (checked using a Stopwatch):
static int CountDigitsInString(int number, int digit)
{
int sum = 0;
while (number != 0)
{
if (number % 10 == digit)
sum++;
number /= 10;
}
return sum;
}
EDIT:
I found a way which is over 4 times as fast as the one above. Before I start, note that this solution is valid only for cases in which you are counting appearances of a digit in consecutive numbers.
It occurred to me that if you counted the number of times the digit "d" appeared in a number "A", then you don't neccessarily have to recount the number of times "d" appears in "A + 1" to know what it is.
For example, if I know that the digit 3 appears 4 times in the number 35312336, I can know for a fact that it will still appear 4 times in the next consecutive number 35312337, without actually counting.
The reason I can do this is that the count would only change in one of three cases:
1) When the last digit of "A - 1" was a 9, "A" can change entirely due to numbers being carried over. This is the only case in which we actually have to count (although you could, theoretically, optimize this further by checking the numbers carried over to see if they affect the total but this strikes me as overly complicated).
2) When the last digit of "A - 1" was "d - 1", we know that the number of times "d" appears in "A" has increased by one.
3) When the last digit of "A - 1" was "d", we know that the number of times "d" appears in "A" has decreased by one.
This means that you only have to count the appearances of "d" in "A" using arithmetical operations in one out of 10 cases!
public static void Run()
{
int digit = 1;
int count = 0;
for (int i = 0; i < 100000; i++)
{
int previousLastDigit = (i - 1) % 10;
if (previousLastDigit == (digit - 1))
count++;
else if (previousLastDigit == 9)
count = CountDigitsInString(i, digit);
else if (previousLastDigit == digit)
count--;
Console.WriteLine(digit + " appears " + count + " times in the number " + i);
}
}
The CountDigitsInString function is the one above.
Here is a little snip with LINQ to give you another way to do it (didn't ran a stopwatch)
var number = 11334511;
var digit = 1;
var digitAsChar = Convert.ToChar(digit.ToString().ToLower());
// Occurence will be 4
var occurence = number.ToString().ToLower().Count(s => s == digitAsChar);
I suggest you use LINQ on string.
Source: https://msdn.microsoft.com/en-us/library/mt693025.aspx
static int CountDigitInString(string number, char digit)
{
int count = number.Count(ns => ns == digit);
return count;
}

Convert numbers in string to English word format

I have a C# method that converts integer numbers into their English word counterparts; however, how can I implement this so it'll work with a string.
For example, if I have the following string:
There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.
I want it to parse it and return:
There was a dog. The dog ate one thousand bones. After eating, he was very sleepy. He slept for twelve hours.
How would I go by taking the numbers out of the sentence, and using the conversion method (below)?
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
I tried doing a foreach, like this, but it didn't work...
var matches = Regex.Matches(myString, "[0-9]+");
foreach(var match in matches)
{
NumberToWords(match);
}
You could use Split and LINQ:
var input = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
var invalidChars = new [] { ',', ';', '-', '.' };
var words = input.Split(' ')
.Select(x =>
{
if (x.All(char.IsDigit) || x.Trim(invalidChars).All(char.IsDigit))
return NumberToWords(int.Parse(x.Trim(invalidChars)));
return x;
});
var output = string.Join(" ", words);
Btw I assume that NumberToWord method is working correctly.
You could use Selman22's approach, but in case you are not using a version of .NET with LINQ, you could use the RegularExpression replace function instead of Match.
http://msdn.microsoft.com/en-us/library/cft8645c(v=vs.110).aspx
You would just need a MatchEvaluator delegate like this:
static string matchEvaluator(Match m)
{
return NumberToWords(int.Parse(m.ToString()));
}
Much more simple way is to use Regex.Replace method utilizing MatchEvaluator.
string input = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
string output = Regex.Replace(input, #"\d+", m => NumberToWords(int.Parse(m.Value)));
Minimal Changes to your existing code:
string s = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
foreach (var match in Regex.Matches(s, "[0-9]+"))
{
string number = match.ToString();
s = s.Replace(number, NumberToWords(int.Parse(number)));
}
Console.WriteLine(s);

converting numbers in to words C# [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How can I convert an integer into its verbal representation?
Can anybody give me a primer code I could work on in converting numbers into words?
Converting numbers to words (ranging from -1000 to +1000)
example: 1000 --> one thousand
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
When I had to solve this problem, I created a hard-coded data dictionary to map between numbers and their associated words. For example, the following might represent a few entries in the dictionary:
{1, "one"}
{2, "two"}
{30, "thirty"}
You really only need to worry about mapping numbers in the 10^0 (1,2,3, etc.) and 10^1 (10,20,30) positions because once you get to 100, you simply have to know when to use words like hundred, thousand, million, etc. in combination with your map. For example, when you have a number like 3,240,123, you get: three million two hundred forty thousand one hundred twenty three.
After you build your map, you need to work through each digit in your number and figure out the appropriate nomenclature to go with it.

Categories

Resources