I just finished project euler #17. I’ll go through my thought process in solving it here, but would love feedback, especially if your solution was different than mine. I’m sure there are many more elegant and simplistic solutions.
The problem goes as follows:
If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.
If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?
NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of “and” when writing out numbers is in compliance with British usage.
My first thought was that I would need to loop over the numbers 1 to 1000 and convert each number into the written word corresponding to that number.
Naturally, I could just make an array containing every written word. But I didn’t want to have to write every number. Instead, I thought about doing something like this:
Without defining every letter, I could make an array of the numbers 1 through 9, and then another array of the letters “one” through “nine”.
If, for example, the number is 547, then I could see if the first number of the given number set matches any numbers from the onesArr. Then I could do the same for the last number. Once it finds a match, I’ll simply grab the value of the onesComp array instead, but at the same index where I found the match in the onesArr.
Although, this wouldn’t work for “twenty”, “thirty”, “forty” etc. It also wouldn’t work with 10 through 19. So, I could make a couple of additional arrays, and do the same thing, depending on the length of the given number.
Something like this would allow me to have a number array for 1–10, and a matching word array for “one”-”ten”. Then the same thing for teens, and tens. The key for me, is having the index values match. So my ones array starts at 1 instead of 0, since we aren’t using zero, and my teens array does include a zero, since that starts at 10, and I’ll be looking for the 0 in the number 10, just like I’ll be looking for the 1 in 11 and the 2 in 12, etc. Then the tens array starts with 2 and twenty. At that point, the next obstacle would be to determine the length of the number. My thought there would be to loop over the array of 1 to 1000. For each item, I would turn the number into a string and then split the string, so it would give me an array of each number. Then, inside and if statement, I would use parseInt() to convert it back into a number and check it against the value of the compare array. That part would probably look something like this:
Here’s the final code:
let ones = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];let teens = ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];let tens = ['twenty', 'thrity', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];let thousands = ['thousand'];let compareOnes = [1,2,3,4,5,6,7,8,9];let compareTeens = [0,1,2,3,4,5,6,7,8,9];let compareTens = [2,3,4,5,6,7,8,9];
let numbers = [];for (var i = 1; i <= 1000; i++) {numbers.push(i);}
let currentCount = 0;
for (var i = 0; i < numbers.length; i++) {let numStr = numbers[i].toString();let numStrArr = numStr.split("");if(numStrArr.length == 1){let digit1 = parseInt(numStrArr[0]);let index = compareOnes.indexOf(digit1);currentCount = currentCount + ones[index];}if(numStrArr.length == 2 && numbers[i]>=20){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let index = compareTens.indexOf(digit1);let index2 = compareOnes.indexOf(digit2);currentCount = currentCount + tens[index] + ones[index2];}if(numStrArr.length == 2 && numbers[i]<20){let digit2 = parseInt(numStrArr[1]);let index2 = compareTeens.indexOf(digit2);currentCount = currentCount + teens[index2];}if(numStrArr.length == 3 && numStrArr[1] == "0" && numStrArr[2] == "0"){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let digit3 = parseInt(numStrArr[2]);let index3 = compareOnes.indexOf(digit1);let index2 = compareTens.indexOf(digit2);let index = compareOnes.indexOf(digit3);currentCount = currentCount + ones[index3]+"hundred";}if(numStrArr.length == 3 && numStrArr[1] == "0" && numStrArr[2] !== "0"){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let digit3 = parseInt(numStrArr[2]);let index3 = compareOnes.indexOf(digit1);let index2 = compareTens.indexOf(digit2);let index = compareOnes.indexOf(digit3);currentCount = currentCount + ones[index3]+"hundred"+"and"+tens[index2]+ones[index];}if(numStrArr.length == 3 && numStrArr[1] == "1"){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let digit3 = parseInt(numStrArr[2]);let index3 = compareOnes.indexOf(digit1);let index2 = compareTeens.indexOf(digit3);currentCount = currentCount + ones[index3]+"hundred"+"and"+teens[index2];}if(numStrArr.length == 3 && numStrArr[1] !== "0" && numStrArr[2] !== "0" && numStrArr[1] !== "1"){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let digit3 = parseInt(numStrArr[2]);let index3 = compareOnes.indexOf(digit1);let index2 = compareTens.indexOf(digit2);let index = compareOnes.indexOf(digit3);currentCount = currentCount + ones[index3]+"hundred"+"and"+tens[index2]+ones[index];}if(numStrArr.length == 3 && numStrArr[1] !== "0" && numStrArr[2] == "0" && numStrArr[1] !== "1"){let digit1 = parseInt(numStrArr[0]);let digit2 = parseInt(numStrArr[1]);let digit3 = parseInt(numStrArr[2]);let index3 = compareOnes.indexOf(digit1);let index2 = compareTens.indexOf(digit2);let index = compareOnes.indexOf(digit3);currentCount = currentCount + ones[index3]+"hundred"+"and"+tens[index2];}if(numStrArr.length == 4){currentCount = currentCount + "one"+"thousand";}}
let newCurrent = currentCount.replace(/undefined/g, "");console.log(newCurrent);console.log(newCurrent.length)
You may notice my bit of regex at the bottom there. I kept having undefined come up, and it was just added in there. I could have tried to figure out why, but it was obvious that all the other words I needed were still in there, so I just wrote a bit of regex to get rid of any undefined in the final string.
Here’s what the final string looks like…well, just the first 75 numbers:
onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourteenfifteensixteenseventeeneighteennineteentwentytwentyonetwentytwotwentythreetwentyfourtwentyfivetwentysixtwentyseventwentyeighttwentyninethritythrityonethritytwothritythreethrityfourthrityfivethritysixthrityseventhrityeightthrityninefortyfortyonefortytwofortythreefortyfourfortyfivefortysixfortysevenfortyeightfortyninefiftyfiftyonefiftytwofiftythreefiftyfourfiftyfivefiftysixfiftysevenfiftyeightfiftyninesixtysixtyonesixtytwosixtythreesixtyfoursixtyfivesixtysixsixtysevensixtyeightsixtynineseventyseventyoneseventytwoseventythreeseventyfourseventyfive