Sean
Sean

Reputation: 23

I keep getting an error that I don't understand

I am trying to create a function that takes a string in it's parameters. It's supposed to determine the highest and lowest numeric values in the string and return them unchanged. Here's my code:

def high_and_low(numbers)
  numbers.split
  numbers.each {|x| x.to_i}
  return numbers.max().to_s, numbers.min().to_s
end

Here's the error:

main.rb:5:in `high_and_low': undefined method `each' for "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6":String (NoMethodError)
    from main.rb:8:in `<main>'

Upvotes: 0

Views: 75

Answers (2)

Danil Speransky
Danil Speransky

Reputation: 30453

You have not changed the value from string to array.

Replace numbers.split with numbers = numbers.split.

Also you will need to change from numbers.each { |x| x.to_i } to numbers.map!(&:to_i). Otherwise you don't save integers anywhere.

BTW you don't have to use () and return (if it's in the end) so you can write [numbers.max.to_s, numbers.min.to_s].

Something like this should work:

def high_and_low(numbers)
  numbers = numbers.split.map(&:to_i)
  [numbers.max, numbers.min].map(&:to_s)
end

high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6") #=> ["542", "-214"]

And bonus (one liner, not that you should write code this way):

def high_and_low(numbers)
  numbers.split.map(&:to_i).sort.values_at(-1, 0).map(&:to_s)
end

high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6") #=> ["542", "-214"]

The other answer is a good approach too so I include it here:

numbers.split.minmax_by { |n| -n.to_i }

Upvotes: 1

the Tin Man
the Tin Man

Reputation: 160551

Ruby has some nice methods available to make this much more simple:

"2 1 0 -1 -2".split.map(&:to_i).minmax
# => [-2, 2]

Breaking it down:

"2 1 0 -1 -2".split       # => ["2", "1", "0", "-1", "-2"]
             .map(&:to_i) # => [2, 1, 0, -1, -2]
             .minmax      # => [-2, 2]

If you want string versions of the values back, compare two integers in a block. minmax will return the values at the corresponding positions in the source array:

"2 1 0 -1 -2".split.minmax{ |a, b| a.to_i <=> b.to_i }
# => ["-2", "2"]

or:

"2 1 0 -1 -2".split.minmax_by{ |a| a.to_i }
# => ["-2", "2"]

minmax and minmax_by do the heavy lifting. The first is faster when there isn't a costly lookup to find the values being compared such as this case where the values are in an array and only needed to_i to compare them.

The *_by version performs a "Schwartzian transform" which basically remembers the values in the block as they're compared so the costly lookup only occurs once. (Many of Enumerable's methods have *_by versions.) These versions of the methods can improve the speed when you want to compare two values that are nested, perhaps in arrays of hashes of hashes, or objects within objects within objects.

Note: When comparing string versions of numbers it's important to convert to a numeric value when comparing. ASCII and strings order differently than numbers, hence the use of to_i.

Upvotes: 0

Related Questions