I have a issue.. Appending - ruby

I have this code:
1 #!/local/usr/bin/ruby
2
3 users = (1..255).to_a
4
5 x = " "
6 y = " "
7 z = " "
8 #a = " "
9
10 count = 1
11 users.each do |i|
12 x << i if count == 1
13 y << i if count == 2
14 z << i if count == 3
15 # if x.length == 60
16 # a << i if count == 1
17 # a << i if count == 2
18 # a << i if count == 3
19 # else
20 # end
21 if count == 3
22 count = 1
23 else
24 count += 1
25 end
26 end
27
28 puts x.length
29 puts y.length
30 puts z.length
31 #puts a.length
32
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
IT WORKS
Example of working code:
[user#server ruby]$ ruby loadtest.rb
86
86
86
[user#server ruby]$
Now what I want it to do is have a failsafe called a as seen above, commented out, What I want is this, if each string contains 60 numbers I want it to append into the a string until there are no more numbers.
When I try to do it with the commented out section it outputs this:
[user#server ruby]$ ruby loadtest.rb
86
86
86
4
[user#server ruby]$ ruby loadtest.rb
WHY?! What am I doing wrong?

What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
After reducing the number of values being iterated for readability, here's what it's doing:
users = (1..5).to_a
x = " "
y = " "
z = " "
count = 1
users.each do |i|
x << i if count == 1 # => " \u0001", nil, nil, " \u0001\u0004", nil
y << i if count == 2 # => nil, " \u0002", nil, nil, " \u0002\u0005"
z << i if count == 3 # => nil, nil, " \u0003", nil, nil
if count == 3
count = 1
else
count += 1
end
end
x # => " \u0001\u0004"
y # => " \u0002\u0005"
z # => " \u0003"
puts x.length
puts y.length
puts z.length
# >> 3
# >> 3
# >> 2
Your code is creating binary inside the strings, not "numbers" as we normally think of them, as digits.
Moving on, you can clean up your logic using each_with_index and case/when. To make the results more readable I switched from accumulating into strings into arrays:
users = (1..5).to_a
x = []
y = []
z = []
users.each_with_index do |i, count|
case count % 3
when 0
x << i
when 1
y << i
when 2
z << i
end
end
x # => [1, 4]
y # => [2, 5]
z # => [3]
puts x.length
puts y.length
puts z.length
# >> 2
# >> 2
# >> 1
The real trick in this is the use of %, which does a modulo on the value.

... if each string contains 60 numbers I want it to append into the a string until there are no more numbers
As written, you are unconditionally appending to x,y,z even after they hit your limit.
You need to add a conditional around this code:
x << i if count == 1
y << i if count == 2
z << i if count == 3
so that it stops appending once it hits your limit.
By the looks of the else block that does nothing, I think you were headed in that direction:
if x.length == 60
a << i if count == 1
a << i if count == 2
a << i if count == 3
else
x << i if count == 1
y << i if count == 2
z << i if count == 3
end
Even that, though, won't do exactly what you want.
You'll want to check the string you are appending to to see if it has hit your limit yet.
I'd suggest refactoring to make it cleaner:
users.each do |i|
target_string = case count
when 1 then x
when 2 then y
when 3 then z
end
target_string = a if target_string.length == 60
target_string << i
if count == 3
count = 1
else
count += 1
end
end

It may be better to use an array instead of string as you are pushing numbers into those variables.
Let me propose a solution which achieves more or less what you are trying to do, but uses few Ruby tricks that may be useful in future.
x, y, z = r = Array.new(3) {[]}
a = []
iter = [0,1,2].cycle
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
p x.size, y.size, z.size
p a.size
Let's define our arrays. Even though I have arrays x, y, and z, they are there only because they were present in your code - I think we just need three arrays, each of which would collect numbers as they are picked from a range of numbers - between 1 to 255 - one by one. x,y,z = r uses parallel assignment technique and is equivalent to x,y,z = r[0],r[1],r[2]. Also, use of Array.new(3) {[]} helps in creating the Array of Array such that when we access r[1] it is initialized with empty array([]) by default.
x, y, z = r = Array.new(3) {[]}
a = []
In order to determine which array the next number picked from range has to be placed in, we will use an Enumerator generated from Enumerable#cycle. This enumerator is special - because it is soft of infinite in nature - and we can keep asking it to give an element by calling next, and it will cycle through the array elements of [0,1,2] - returning us 0,1,2,0,1,2,0,1,2... infinitely.
iter = [0,1,2].cycle
Next, we will iterate through the range of numbers 1..255. During each iteration, we will check whether all the 3 arrays in which we are collecting number have desired size of 60 with the help of Enumerable#all? - if so, we will append the number to array a - else we will assign it to one of the sub arrays of r based on the array index returned by iter enumerator.
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
Finally, we print the size of each of the array.
p x.size, y.size, z.size
#=> 60, 60, 60
p a.size
#=> 75

Related

How does recursion work in ruby?

I have this method which switches the number digit 5 with 7.
def switch_digit(num)
if num <= 0
return 0
end
digit = num % 10
if (digit == 5)
digit = 7
end
return switch_digit(num/10) * 10 + digit
end
switch_digit(5952)
Can someone explain why once the method hits the base case it doesn't return 0?
How does this recursive method actually work? Does it append the returned digit with the next digit?
I added a little change to your code, to be aware it's working.
Finally I also expected the value of the method was 0, but it is not.
The end is reached, but the returned value is not 0. Why?
def switch_digit(num, array)
if num <= 0
array << num
p array
puts "The end"
return 0
end
digit = num % 10
array << [digit, num]
if (digit == 5)
digit = 7
end
return p switch_digit(num/10, array) * 10 + digit
end
p "returned value = " + switch_digit(123456789, Array.new).to_s
Which outputs:
#=> [[9, 123456789], [8, 12345678], [7, 1234567], [6, 123456], [5, 12345], [4, 1234], [3, 123], [2, 12], [1, 1], 0]
#=> The end
#=> 1
#=> 12
#=> 123
#=> 1234
#=> 12347
#=> 123476
#=> 1234767
#=> 12347678
#=> 123476789
#=> "returned value = 123476789"
The base case returns 0, but the overall result is determined by the return switch_digit(num/10) * 10 + digit
Follow the code through with a smaller example e.g. switch_digit(15):
num <= 0 # no
digit = num % 10 # 5
digit == 5 # yep, so swap it for a 7
return switch_digit(num/10) * 10 + 7
num/10 is 1 so what does the recursive switch_digit(1) evaluate to?
num <= 0 # no
digit = num % 10 # 1
digit == 1 # so leave it unchanged
return switch_digit(num/10) * 10 + 1
num/10 is 0 so now we hit the base case
switch_digit(15) == switch_digit(1) * 10 + 7
switch_digit(1) == switch_digit(0) * 10 + 1
switch_digit(0) == 0 # the base case
working back up, plugging in values from lower down results:
switch_digit(1) == 0 * 10 + 1 == 1
switch_digit(15) == 1 * 10 + 7 == 17
I'd also add that there's nothing specific to Ruby about how recursion is handled here. Any other descriptions of recursion, or classic example such as a recursive factorial function should help you get a better understanding.

Code wars: Flap Display with while loops

I'm trying to work through a level 5 kata by using while loops. Essentially the problem is to turn each letter rotors[n] number of times and then move on to the next rotors number until you get an output word.
flap_display(["CAT"],[1,13,27])
should output ["DOG"]
Here's what I have so far
def flap_display(lines, rotors)
stuff = "ABCDEFGHIJKLMNOPQRSTUVWXYZ?!##&()|<>.:=-+*/0123456789"
i = 0
j = 0
new_word = lines
while i < rotors.length
while j < new_word[0].length
new_word[0][j] = stuff[stuff.index(new_word[0][j]) + rotors[i]]
j += 1
end
i += 1
j = 0
end
new_word
end
This technically traverses the stuff string and assigns the right letters. However it fails two important things: it does not skip each letter when it rotates to the correct position (C should stop rotating when it hits D, A when it hits O etc) and it does not account for reaching the end of the stuff list and eventually returns a nil value for stuff[stuff.index(new_word[0][j]) + rotors[i]]. How can I fix these two problems using basic loops and enumerables or maybe a hash?
A fuller statement of the problem is given here. This is one Ruby-like way it could be done.
FLAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!##&()|<>.:=-+*/0123456789"
NBR_FLAPS = FLAPS.size
def flap_display(str, rot)
rot_cum = rot.each_with_object([]) { |n,a| a << a.last.to_i + n }
str.gsub(/./) { |c| FLAPS[(c.ord + rot_cum.shift - 65) % NBR_FLAPS] }
end
flap_display("CAT", [1,13,27])
#=> "DOG"
flap_display("DOG", [-1,-13,-27])
#=> "CAT"
flap_display("CAT", [5,37,24])
#=> "H*&"
'A'.ord #=> 65 and rot_cum contains the cumulative values of rot:
arr = [1, 13, 27]
rot_cum = arr.each_with_object([]) { |n,a| a << a.last.to_i + n }
#=> [1, 14, 41]
I've written a.last.to_i rather than a.last to deal with the case where a is empty, so a.last #=> nil, meaning a.last.to_i => nil.to_i => 0. See NilClass#to_i. Those opposed to such trickery could write:
rot_cum = arr.drop(1).each_with_object([arr.first]) { |n,a| a << a.last + n }

How to refactor consecutive, independent, if statements in this algorithm?

The following method generates 81 Cell objects that each have their distinct row, column, and blocks. It uses an algorithm that changes those attributes based on the Cell that's currently be generated.
The 1.times do portion is implemented to skip that block of if statements on the first loop inside the until loop, when the cell_counter is 0. How do I make this more elegant?
def initialize_default_cells
cell_counter, row, column = 0,0,0
block = 1
until cell_counter == 81
1.times do
break if cell_counter == 0
if cell_counter % 1 == 0
column += 1
end
if cell_counter % 3 == 0
block += 1
end
if cell_counter % 9 == 0
column -= 9
row += 1
block -= 3
end
if cell_counter % 27 == 0
block += 3
end
end
#cells << Cell.new(ROW_ID[row], COLUMN_ID[column], block)
cell_counter += 1
end
end
I concluded that it was easiest to calculate row, column and block from scratch for each i = 0,..,80.
def initialize_default_cells
(0..80).each do |i|
#cells << Cell.new(ROW_ID[i/9], COLUMN_ID[i%9], 1 + (i%9)/3 + 3*(i/27))
end
end
The key for COLUMN_ID (i%9) is reduced from i-9*(i/9) and the last argument (1 + (i%9)/3 + 3*(i/27)) is reduced from 1 + i/3 - 3*(i/9) + 3*(i/27)
Consider three examples.
i=0
#cells << Cell.new(ROW_ID[0/9], COLUMN_ID[0%9], 1 + (0%9)/3 + 3*(0/27))
#=> << Cell.new(ROW_ID[0], COLUMN_ID[0], 1)
i=6
#cells << Cell.new(ROW_ID[6/9], COLUMN_ID[6%9], 1 + (6%9)/3 + 3*(6/27))
#=> << Cell.new(ROW_ID[0], COLUMN_ID[6], 3)
i=29
#cells << Cell.new(ROW_ID[29/9], COLUMN_ID[29%9], 1 + (29%9)/3 + 3*(29/27))
# << Cell.new(ROW_ID[3], COLUMN_ID[2], 4)
When i=6, 6/3 #=> 2 is the number of positive numbers that are divisible by 3, 6/9 #=> 0 is the number of positive numbers that are divisible by 9 and 6/27 #=> 0 is the number of positive numbers that are divisible by 27. The arguments of Cell::new are then computed with these values.

How do I use a hash to modify the values of an Array?

I am building a base converter. Here is my code so far:
def num_to_s(num, base)
remainders = [num]
while base <= num
num /= base #divide initial value of num
remainders << num #shovel results into array to map over for remainders
end
return remainders.map{|i| result = i % base}.reverse.to_s #map for remainders and shovel to new array
puts num_to_s(40002, 16)
end
Now it's time to account for bases over 10 where letters replace numbers. The instructions (of the exercise) suggest using a hash. Here is my hash:
conversion = {10 => 'A', 11 => 'B', 12 => 'C', 13 => 'D', 14 => 'E', 15 => 'F',}
The problem is now, how do I incorporate it so that it modifies the array? I have tried:
return remainders.map{|i| result = i % base}.map{|i| [i, i]}.flatten.merge(conversion).reverse.to_s
In an attempt to convert the 'remainders' array into a hash and merge them so the values in 'conversion' override the ones in 'remainders', but I get an 'odd list for Hash' error. After some research it seems to be due to the version of Ruby (1.8.7) I am running, and was unable to update. I also tried converting the array into a hash outside of the return:
Hashes = Hash[remainders.each {|i, i| [i, i]}].merge(conversion)
and I get an 'dynamic constant assignment' error. I have tried a bunch of different ways to do this... Can a hash even be used to modify an array? I was also thinking maybe I could accomplish this by using a conditional statement within an enumerator (each? map?) but haven't been able to make that work. CAN one put a conditional inside an enumerator?
Yes, you could use a hash:
def digit_hash(base)
digit = {}
(0...[10,base].min).each { |i| digit.update({ i=>i.to_s }) }
if base > 10
s = ('A'.ord-1).chr
(10...base).each { |i| digit.update({ i=>s=s.next }) }
end
digit
end
digit_hash(40)
#=> { 0=>"0", 1=>"1", 2=>"2", 3=>"3", 4=>"4",
# 5=>"5", 6=>"6", 7=>"7", 8=>"8", 9=>"9",
# 10=>"A", 11=>"B", 12=>"C", ..., 34=>"Y", 35=>"Z",
# 36=>"AA", 37=>"AB", 38=>"AC", 39=>"AD" }
There is a problem in displaying digits after 'Z'. Suppose, for example, the base were 65. Then one would not know if "ABC" was 10-11-12, 37-12 or 10-64. That's detail we needn't worry about.
For variety, I've done the base conversion from high to low, as one might do with paper and pencil for base 10:
def num_to_s(num, base)
digit = digit_hash(base)
str = ''
fac = base**((0..Float::INFINITY).find { |i| base**i > num } - 1)
until fac.zero?
d = num/fac
str << digit[d]
num -= d*fac
fac /= base
end
str
end
Let's try it:
num_to_s(134562,10) #=> "134562"
num_to_s(134562, 2) #=> "100000110110100010"
num_to_s(134562, 8) #=> "406642"
num_to_s(134562,16) #=> "20DA2"
num_to_s(134562,36) #=> "2VTU"
Let's check the last one:
digit_inv = digit_hash(36).invert
digit_inv["2"] #=> 2
digit_inv["V"] #=> 31
digit_inv["T"] #=> 29
digit_inv["U"] #=> 30
So
36*36*36*digit_inv["2"] + 36*36*digit_inv["V"] +
36*digit_inv["T"] + digit_inv["U"]
#=> 36*36*36*2 + 36*36*31 + 36*29 + 30
#=> 134562
The expression:
(0..Float::INFINITY).find { |i| base**i > num }
computes the smallest integer i such that base**i > num. Suppose, for example,
base = 10
num = 12345
then i is found to equal 5 (10**5 = 100_000). We then raise base to this number less one to get the initial factor:
fac = base**(5-1) #=> 10000
Then the first (base-10) digit is
d = num/fac #=> 1
the remainder is
num -= d*fac #=> 12345 - 1*10000 => 2345
and the factor for the next digit is:
fac /= base #=> 10000/10 => 1000
I made a couple of changes from my initial answer to make it 1.87-friedly (I removed Enumerator#with_object and Integer#times), but I haven't tested with 1.8.7, as I don't have that version installed. Let me know if there are any problems.
Apart from question, you can use Fixnum#to_s(base) to convert base.
255.to_s(16) # 'ff'
I would do a
def get_symbol_in_base(blah)
if blah < 10
return blah
else
return (blah - 10 + 65).chr
end
end
and after that do something like:
remainders << get_symbol_in_base(num)
return remainders.reverse.to_s

Use Each on an array to iterate over and return the index of greater values

a = [2,2,4,8,9]
ind = 1
a.each do |x|
if a[ind] < a[x]
puts x
end
end
How can I use "each" on an array to iterate over and return the index of all values greater than a certain value in Ruby?
I would like to iterate over the given array a = [2,2,4,8,9]. I want to iterate over the entire array and, using a conditional, put out all values where a[ind] < a[x].
I receive the error comparison of fixnum nil failed. - How can I resolve this?
I did try this as well, seting a range for the process:
a = [ 2,2,3,4,5]
x = 0
while x >= 0 && x <= 4
a.each do |x|
if a[1] < a[x]
puts x
end
end
x += 1
end
You want to select all elements whose index is less than themselves. You can just say exactly that in Ruby:
a.select.with_index {|el, idx| idx < el }
or even
a.select.with_index(&:>)
When you are iterating over an array using each the x denotes the value of the item, not its position:
a = [2,2,4,8,9]
ind = 1
a.each do |x|
if a[ind] < x
puts x
end
end
# prints:
# 4
# 8
# 9
Update:
If you want to print the indexes of the elements with value greater than the value, you should use each_with_index:
a = [2,2,4,8,9]
ind = 1
a.each_with_index do |x, i|
if a[ind] < x
puts i
end
end
# prints:
# 2
# 3
# 4
def filtered_index(array,n)
array.each_with_index{|e,i| puts i if e > n}
end

Resources