In Ruby I have three nested loops:
array.each do |a|
array.each do |b|
array.each do |c|
puts a * b * c
end
end
end
How can I optimize this code, if the number of nested loops can be increased to 5-10 and more iterations?
Example:
array.each do |a|
array.each do |b|
array.each do |c|
array.each do |d|
array.each do |e|
array.each do |f|
puts a * b * c * d * e * f
end
end
end
end
end
end
You can do something like this:
array.repeated_combination(array.size).each do |combination|
puts combination.reduce(:*)
end
Array#repeated_combination returns an enumerator that yields all possible combinations.
Because this method generates all combinations before printing any output is might take a while depending on the size of the array. Keep in mind that the number of possible combinations increases quite fast: O(nⁿ) with n being the number of elements in the array.
Here are two other ways (though I prefer #spickermann's answer)
#1
array = [1,2,3]
n = 4
arr = array.product(*[array]*(n-1)).map { |arr| arr.reduce(:*) }
arr.size #=> 81 = arr.size**n
arr.each { |e| puts e }
1
2
3
2
4
6
3
6
9
...
54
27
54
81
If you just want to print the products, replace map with each and arr.reduce(:*) with puts arr.reduce(:*).
#2
sz = array.size
(0...sz**n).map { |i| i.to_s(sz)
.rjust(n,'_')
.chars
.reduce(1) { |t,e| t * (e=='_' ? 1 : array[e.to_i]) }
}
Related
I'm trying to run standard ruby training programs, but I had a problem with this program, please take a look. Thank you very much!
Code:
q = 9999 #last 4-digit number
while q > 1000 #from 9999 to 1000, for exemple, the cycle has arrived to 6784
d = q.to_s.chars.map(&:to_i) #transform 6784 to array [6, 7, 8, 4]
p = d # create sample array with [6, 7, 8, 4]
tmp = p[0]; # tmp = 6;
p[0] = p[3]; # 6 = 4;
p[3] = tmp; # 4 = 6
g = p.join.to_i # transform [4, 7, 8, 6] to 4786
f = q - g # 6784 - 4786
if f == 27 # i need to find the smallest 4-digit number that decreases by 27 when moving its last digit to the first position
puts q #print 4-digit number that decreases by 27 when moving its last digit to the first position
end
q = q - 1;
end
But the result does not appear, it is because it is not, or somewhere a mistake.
In general, the condition of the task is:
Find the smallest 4-digit number that decreases by 27 when you move its last digit to the first position. (Use the find or detect method). Thank You!
I will first create a helper method to convert an array of digits to an integer.
def digits_to_int(arr)
arr.reduce { |n,d| n*10 + d }
end
For example,
digits_to_int [1,2,3,4]
#=> 1234
This tends to be faster than arr.join.to_i (see sawa's answer here).
We can then simply compute
(1..).find { |n| n-27 == digits_to_int(n.digits.rotate.reverse) }
#=> 30
See Enumerable#reduce (a.k.a. inject), "Endless range", Integer#digits, Array#rotate and Array#reverse.
Here is an example calculation.
n = 243
a = n.digits
#=> [3,4,2]
b = a.rotate
#=> [4,2,3]
c = b.reverse
#=> [3,2,4]
d = digits_to_int(c)
#=> 324
n - 27 == d
#=> 243 - 27 == 324 => false
and another
n = 30
a = n.digits
#=> [0,3]
b = a.rotate
#=> [3,0]
c = b.reverse
#=> [0,3]
d = digits_to_int(c)
#=> 3
n - 27 == d
#=> 30 - 27 == 3 => true
I would define a method to "rotate" the number using string manipulation.
def rotate_number_one_digit(n)
s = n.to_s
"#{s[-1]}#{s[0..-2]}".to_i
end
Then I would use #upto to deal with the iteration.
1000.upto(9999) do |x|
end
Each time around you'll check that the "rotated" number plus 27 equals x. If so, print it and break the loop to prevent further unnecessary iteration.
1000.upto(9999) do |x|
if rotate_number_one_digit(x) + 27 == x then
puts x
break
end
end
Or we can just use the #find method from Enumerable.
1000.upto(9999).find { |x| rotate_number_one_digit(x) + 27 == x }
Or using break to return a value from the loop.
1000.upto(9999) { |x|
break x if rotate_number_one_digit(x) + 27 == x
}
I want to my n to multiply with next number for example if n=99 i want it to 9*9 and then return a result, and then i want the result (9*9 = 81 then 8*1 = 8) to multiply until it becomes 1 digit.
Here's my code:
def persistence(n)
if n <= 9
puts n
else
n.to_s.each_char do |a|
a.to_i * a.to_i unless n < 9
puts a.to_i
end
end
end
and i want it to return this:
persistence(39) # returns 3, because 3*9=27, 2*7=14, 1*4=4
# and 4 has only one digit
persistence(999) # returns 4, because 9*9*9=729, 7*2*9=126,
# 1*2*6=12, and finally 1*2=2
persistence(4) # returns 0, because 4 is already a one-digit number
def persistence(n)
i = 0
while n.to_s.length != 1
n = n.to_s.each_char.map(&:to_i).reduce(:*)
i +=1
end
i
end
persistence(39) #=> 3
persistence(999) #=> 4
Other version:
def p(n, acc)
return acc if n <= 9
p(n.to_s.each_char.map(&:to_i).reduce(:*), acc+1)
end
def persistence(n)
p(n, 0)
end
I will leave the breaking down of method and understanding what's happening and what is the difference b/w two variations to you. Will love to see your comment explaining it.
def persistence(n)
0.step.each do |i|
break i if n < 10
n = n.digits.reduce(:*)
end
end
persistence 4 #=> 0
persistence 39 #=> 3
persistence 999 #=> 4
persistence 123456789123456789 #=> 2
Regarding the last result, note that 2*5*2*5 #=> 100.
This question already has answers here:
Blocks and yields in Ruby
(10 answers)
Closed 8 years ago.
I'm trying to understand how yield works in Ruby?
def ablock
i = 1
j = 2
yield(i, j, 3, 4)
end
ablock do |x| puts x
end
This gives me an output of -
1
2
3
4
But,
def ablock
i = 1
j = 2
yield(i, j, 3, 4)
end
ablock do |x,y| puts x, y
end
Gives me only
1
2
as the output. Why don't 3 & 4 print?
The answer is pretty simple. You have defined your block method correctly but when you go to give it a code block you only give it one variable to hold 4 objects. Instead, try giving it a variable for each object you are yielding!
def ablock
i=1
j=2
yield(i,j,3,4)
end
ablock do |i,j,k,l|
puts i
puts j
puts k
puts l
end
If you would only like to use one variable in your code block you have to do multiple yield statements(one for each object).
def ablock
i=1
j=2
yield(i)
yield(j)
yield(3)
yield(4)
end
ablock do |i|
puts i
end
Happy coding!
I have a for loop with an if elsif statement inside. On the first if, if the condition is met I want it to stop there and go on to the next iteration of the loop.
This is a very simplified version of what I am trying to do:
array = [1,2,3,4,"x"]
for i in 0..(array.count -1)
if array[i] == "x"
#start next for loop iteration without executing the elsif
elsif array[i] < 3
puts "YAY!"
end
end
What I am exactly trying to do is iterating through an array which all but one of the elements are integers but one of them is a string. On the string element, I need the loop (whatever kind is best) to skip the rest of the code and go to the next iteration of the loop. This is important because the second if statement uses an 'array_element < 11 condition' so if it runs that on the string element I get "comparison of String with 11 failed"
so I would want arr[x][3] this is what i tried but it gives me 8 8 8 8 instead of a single 8.
arr = [[1,2,3,"4"], [5,6,7,8], [9,10,11,12]]
arr.each{|x|
x.each {|i|
next if x[3].instance_of? String
if x[3] < 12 puts x[3]
end
}
}
Ok this works!! Thank you iAmRubuuu!!
arr = [1,2,3,"4"], [5,6,7,8], [9,10,11,12], [13,14,15,"16"], [17,18,19,20]]
arr.each_with_index{|x, i|
next if x.last.instance_of? String
if x.last < 21
puts x.last
end
}
give me the output
8
12
20
Don't use for in, use each.
(0..10).each do |i|
next if i == 5
if i == 10
puts "YAY!"
end
end
As per your edit, hope the below one you are looking for:
arr = [1, 2, 3, "11", 11]
arr.each do |x|
next if x.instance_of? String
puts "#{x} is #{x.class}"
end
Output:
1 is Fixnum
2 is Fixnum
3 is Fixnum
11 is Fixnum
EDIT
Code:
arr = [[1,2,3,"4"], [4,5,6,7], [8,9,10,11]]
arr.each{|x|
x.each{ |i|
next if i.instance_of? String
puts "#{i} is #{i.class}"
}
}
Output:
1 is Fixnum
2 is Fixnum
3 is Fixnum
4 is Fixnum
5 is Fixnum
6 is Fixnum
7 is Fixnum
8 is Fixnum
9 is Fixnum
10 is Fixnum
11 is Fixnum
V_1(from your first comment in my answer post)
arr = [[1,2,3,"4"], [4,5,6,7], [8,9,10,11]]
puts arr[1].last,arr.last.last
Output:
7
11
V_2(from your first comment in my answer post)
arr = [[1,2,3,"4"], [4,5,6,7], [8,9,10,11]]
arr.each_with_index{ |x,i|
next if i == 0
#p x,i
p "last element of inner array:#{x.last}"
}
Output:
"last element of inner array:7"
"last element of inner array:11"
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.