ror dev newb ror dev newb - 1 year ago 36
Ruby Question

Project Euler Task 17. Find number of all letters from a range of numbers

The problem:

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?

My solution:

require 'humanize'
arr, total = [], 0
(1..1000) {|x| arr << x.delete(" ").delete("-")}.map {|y| total += y.length }
p total

Other solution privided, by @Arif in other question, gives the right answer (according to Project Euler):

p (1..1000)" -", "").size

Why my solution is wrong? From my knowledge, these two implementations should give the same output...

Found one more strange thing.
If I change 1000 to any number up to 20 and implementations looks like this:

(1..20) {|x| arr << x.delete(" ").delete("-")}.map {|y| total += y.length }

p (1..20)" -", "").size

I receive the same answer.....? What?

Answer Source

Your problem is that you are messing map with each.

              .each {|x| arr << x.delete(" ").delete("-") } # ??? each
              .map {|y| total += y.length }                 # ??? map

The call to Enumerable#each returns the original Enumerable, not arr or whatever you’ve expected. Yes, you fulfilled arr, but the latter map is called on the original array, having all these spaces and dashes.

The correct way to implement it your way:

              .map { |x| x.delete(" ").delete("-") } # sic!, no need for arr
              .each { |y| total += y.length } # no need to map here

Or, more ruby idiomatic:

              .map { |x| x.delete(" ").delete("-") }
              .reduce(:+) # reduce by summing everything up

Or, even better (to_a is redundant here):

         .reduce(0) { |memo, x| memo + x.delete(" ").delete("-").length }