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>'
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 }
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.
Related
Not sure what I'm doing incorrect but I seem to be getting it woefully wrong.
The question is, you are given a string of space separated numbers, and have to return the highest and lowest number.
Note:
All numbers are valid Int32, no need to validate them.
There will always be at least one number in the input string.
Output string must be two numbers separated by a single space, and highest number is first.
def high_and_low(numbers)
# numbers contains a string of space seperated numbers
#return the highest and lowest number
numbers.minmax { |a, b| a.length <=> b.length }
end
Output:
`high_and_low': undefined method `minmax' for "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6":String
minmax is not implemented for a string. You need to split your string into an array first. But note that split will return an array of strings, not numbers, you will need to translate the strings to integers (to_i) in the next step.
Because minmax returns the values in the opposite order than required, you need to rotate the array with reverse and then just join those numbers with whitespace for the final result.
numbers = "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"
def high_and_low(numbers)
numbers.split.minmax_by(&:to_i).reverse.join(' ')
end
high_and_low(numbers)
#=> "542 -214"
How about:
numbers_array = numbers.split(' ')
"#{numbers_array.max} #{numbers_array.min}"
If you're starting with a string of numbers you may have to cast the .to_i after the call to split.
In that case:
numbers_array = numbers.split(' ').map { |n| n.to_i }
"#{numbers_array.max} #{numbers_array.min}"
As you're starting with a String, you must turn it into an Array to cast minmax on it.
Also, make sure to compare Integers by casting .map(&:to_i) on the Array; otherwise you'd compare the code-point instead of the numerical value.
def get_maxmin(string)
string.split(' ')
.map(&:to_i)
.minmax
.reverse
.join(' ')
end
There is no need to convert the string to an array.
def high_and_low(str)
str.gsub(/-?\d+/).
reduce([-Float::INFINITY, Float::INFINITY]) do |(mx,mn),s|
n = s.to_i
[[mx,n].max, [mn,n].min]
end
end
high_and_low "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"
#=> [542, -214]
Demo
This uses the form of String#gsub that has one argument and no block, so it returns an enumerator that I've chained to Enumerable#reduce (a.k.a. inject). gsub therefore merely generates matches of the regular expression /-?\d+/ and performs no substitutions.
My solution to this kata
def high_and_low(numbers)
numbers.split.map(&:to_i).minmax.reverse.join(' ')
end
Test.assert_equals(high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"), "542 -214")
#Test Passed: Value == "542 -214"
Some docs about methods:
String#split Array#map Array#minmax Array#reverse Array#join
More about Symbol#to_proc
numbers.split.map(&:to_i) is same as number.split.map { |p| p.to_i }
But "minmax_by(&:to_i)" looks better, for sure I guess.
Typically when you want to have a counter, you can do something like this:
["a","b","c"].each.with_index(0) do |_case, i|
puts i
end
=> 0
=> 1
=> 2
We increment from 0. 0 is represented as a Fixnum in Ruby.
000 is also represented as a Fixnum in Ruby. But I need to increment as 000,001,002,003,etc. But instead it increments from 000 to 1:
["a","b","c"].each.with_index(000) do |_case, i|
puts i
end
=> 0
=> 1
=> 2
How get I increment 000 as 001,002,003,etc?
You can use the formatted printing functions printf and sprintf:
["a","b","c"].each_with_index { |_case, i| printf("%03d\n", i) }
000
001
002
If the goal is to print padded numbers then Ruby can do this for you by using string ranges. A few ways to do this:
puts ('001'..'005').to_a
or
('001'..'005').each { |n| puts n }
or
puts ('001'..'999').take(5)
All of which print:
#001
#002
#003
#004
#005
I would like to generate all possible way of arranging of a certain number of elements number_of_elements. For now, I just want to print every possibility up to the possibility upto.
Edit: Say number_of_elements is 3, then I want all possible ways of arranging 0, 1 and 2. A number can appear 0 or many times, and order is important. So 0 != 00 != 01 != 10 != 11.
For example, all_combinations(3, 14) should print:
0
1
2
00
01
02
10
11
12
20
21
22
000 # updated. I originally put 100 here by mistake.
001
...
I tried this:
def all_combinations(number_of_elements, upto)
0.upto upto do |n|
puts n.to_s(number_of_elements)
end
end
all_combinations(3, 10)
My idea is to get all integers, convert them to base number_of_elements and interpret that number as the possibilities.
It almost works, except that I am missing some elements.
(This is the output I get with the code above) :
0
1
2
# 00 missing
# 01 missing
# 02 missing
10
11
12
20
21
22
# 0.. elements missing
100
101
...
Any idea or other simple method to get those?
Confer this question. The following is a slight modification of my answer there.
class Numeric
def sequence b
s, q = "", self
(q, r = (q - 1).divmod(b)) && s.prepend(r.to_s) until q.zero?
s
end
end
def foo(number_of_elements, upto)
1.upto upto do |n|
puts n.sequence(number_of_elements)
end
end
foo(3, 14)
Result:
0
1
2
00
01
02
10
11
12
20
21
22
000
001
You can do this using Array#repeated_permuation:
def all_combinations(nbr, upto)
(1..nbr).each_with_object([]) do |n, arr|
arr.concat(('0'...nbr.to_s).to_a
.repeated_permutation(n)
.to_a) if arr.size < upto
end.first(upto).each { |e| puts e.join }
end
all_combinations(3, 14)
0
1
2
00
01
02
10
11
12
20
21
22
000
001
If number_of_elements is large, this approach has the disadvantage that a substantial number of permutations may be added to the array arr, but not used. One alternative is to create an enumerator object which can be used to enumerate repeated permutations using Enumerator#next and Enumerator#peek, enumerating exactly upto values. I showed how to do that for permutation here and explained how it would be even easier for repeated_permutation.
Probably the cleanest way to attack this is--unfortunately, if you're not versed in recursion--with a recursive method.
You want to try every possible digit in every possible place, basically.
def all_combinations(s, number_of_elements, upto)
if number_of_elements < 1
puts s
return
end
0.upto upto - 1 do |n|
all_combinations(s + n.to_s, number_of_elements - 1, upto)
end
end
all_combinations("", 3, 10)
The premise is that, wherever you've gotten to in the process (s; imagine it's 12), you want to append all possible digits to it and continue. But, we don't want to go forever, and each digit we place gets us closer, so reduce number_of_elements; when number_of_elements is zero, we have nothing else to do, so it's time to print.
A couple of issues:
- upto doesn't know how big it is. If you call all_combinations("", 3, 500), you'll get badly-formed strings.
- As mentioned, if you're just learning programming, recursion might not be the best place to dive in.
- Likewise, if this is for homework and you haven't covered recursion, this would be a good sign that you're asking random people on the Internet for help, which may not result in an ideal grade.
With those caveats, though, I think this is the most straightforward approach. Everything else that comes to mind requires keeping track of positions and recent outputs manually, which...ick.
Here is a concise way of doing it using repeated_permutations:
def all_combinations(number_of_elements, upto)
elements = (0...number_of_elements).map(&:to_s)
combinations = (1..1.0/0).lazy.flat_map do |i|
elements.repeated_permutation(i).map do |permutation|
permutation.join
end
end
puts combinations.take(upto).to_a
end
all_combinations(3, 14)
0
1
2
00
01
02
10
11
12
20
21
22
000
001
Here's another way of doing this, using String#to_i and Fixnum#to_s, each with the argument base (which defaults to 10) set equal to the "number of elements":
def all_combinations(n_elements, upto)
s = '10'
upto.times.with_object([]) do |_,arr|
arr << s
candidate = (s.to_i(n_elements)+1).to_s(3)
(candidate = '1'+'0' * s.size) unless candidate[0] == '1'
s = candidate
end.map { |str| str[1..-1] }
end
all_combinations(3, 15)
#=> ["0", "1", "2",
# "00", "01", "02",
# "10", "11", "12",
# "20", "21", "22",
# "000", "001", "002"]
Why does Ruby hash an integer n to 2 * n + 1?
>> [0,1,2,3].each {|x| puts x.hash}
1
3
5
7
I can see that you don't always need to have complicated hashes, especially for simple objects. But why the 'double and add 1' rule as opposed to doing what Python does, which is to hash integers to themselves?
>>> map(hash,[0,1,2,3])
[0, 1, 2, 3]
Is there a reason?
Integers are objects, so they have an object_id. But there is an infinite number of integers. Seemingly, no room for other objects. How does Ruby pull this off?
10.times{|i| puts i.object_id}
Output:
1
3
5
7
9
11
13
15
17
19
Integers take all odd object_id's, the rest of the objects go in between, they use the even numbers. The conversion from object_id (and hash) to integer (and vice versa) is very easy: chop the rightmost 1 bit (or add it).
brand new to Ruby, and love it. Just playing around with the below code:
public
def highest
highest_number = 0
each do |number|
number = number.to_i
highest_number = number if number > highest_number
puts highest_number
end
end
array = %w{1 2 4 5 3 8 22 929 1000 2}
array.highest
So at the moment the response I get is:
1
2
4
5
5
8
22
929
1000
1000
So it puts the array first, then the highest number from the array as well. However all I want it to is put the highest number only...
I have played around with this and can't figure it out! Sorry for such a newbie question
The problem is that you have the puts statement inside the each loop, so during every iteration it prints out what the highest number currently is. Try moving it outside the each loop so that you have this:
public
def highest
highest_number = 0
each do |number|
number = number.to_i
highest_number = number if number > highest_number
end
puts highest_number
end
array = %w{1 2 4 5 3 8 22 929 1000 2}
array.highest
Which produces the desired output:
1000
You could also save yourself some trouble by using max_by:
>> a = %w{1 2 4 5 3 8 22 929 1000 2}
=> ["1", "2", "4", "5", "3", "8", "22", "929", "1000", "2"]
>> m = a.max_by { |e| e.to_i }
=> "1000"
You could also use another version of max_by:
m = a.max_by(&:to_i)
to avoid the extra noise of the "block that just calls a method".
But this is probably a Ruby blocks learning exercise for you so using existing parts of the standard libraries doesn't count. OTOH, it is good to know what's in the standard libraries so punting to max_by or max would also count as a learning exercise.
You can do this instead and avoid the highest_number variable.
array = %w{1 2 4 5 3 8 22 929 1000 2}
class Array
def highest
collect { |x| x.to_i }. \
sort. \
last.to_i
end
end
array.highest # 1000
The collect { |x| x.to_i } can also be written as collect(&:to_i) in this case.