Given a number, my code should return all the even numbers between 1 and the number, and print them in the following format:
22
4444
666666
etc...
This is the code so far:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.each {|item| return (item.to_s * item)}
end
With any number greater than four, it will only return the following:
22
I think that this may have something to do with the return in the block. However, when using print or puts, this returns an individual array element as follows:
[2]
Ideas for a way around this so that I can achieve the desired output?
This code fixes your issue:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.map {|item| (item.to_s * item)}
end
Note that I'm using map instead of each, and I'm not using a return. The return meant that you didn't actually finish looping over the numbers... as soon as you got to 2 you returned from the function.
map is what you want if you want to build up a new array with the results.
EDIT
A little more cleanup:
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}
end
The arr = is unnecessary. Your block in a select should just return true or false... you could also use just i % 2 == 0 in there, but even? happens to exist. Also, the parentheses around item.to_s * item are unnecessary.
EDIT 2
Per comments below, if you want a single string, maybe this is what you're looking for (added .join("\n")):
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
EDIT 3
When returning a string, you can also skip the n==1 special case, since joining an empty array will just return an empty string:
def pattern(n)
(1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
Your code doesn't work because it returns when it reaches the first value. See:
def pattern n
return "" if n == 1
(1..n).select { |i|
i if i % 2 == 0
}.each { |item|
return (item.to_s * item) # You are returning here!
}
end
As a suggestion, you could simplify your code to:
def pattern n
(2..n).step(2) { |n| puts n.to_s * n }
end
or --even better IMO-- you return an array with all results and let the caller decide what to do with it:
def pattern n
(2..n).step(2).map { |n| n.to_s * n }
end
Here is another way in which you can tackle the problem, by employing Integer#times:
def pattern n
(2..n).each do |i|
next if i.odd?
i.times { print i }
puts
end
end
pattern 8
#=>
# 22
# 4444
# 666666
# 88888888
Related
I'm currently learning ruby and I wrote this piece of code :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr.first + rec_sum(num_arr.shift)
end
end
puts rec_sum(multi_gen)
That should return the sum of all 3 and 5 multiples up to 1000.
But I get an error :
myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
from villani.rb:17:in `rec_sum'
from villani.rb:21:in `<main>'
But when I re-write it like this :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr[0] + rec_sum(num_arr[1..num_arr.last])
end
end
puts rec_sum(multi_gen)
I don't get the error.
So why is my first rec_sum functions interpretting my Array as a Fixnum in the first case?
The issue is in the recursive call:
rec_sum(num_arr.shift)
Array#shift returns the shifted element, not the remaining array. You should explicitly pass the array as an argument to recursive call:
rec_sum(num_arr[1..-1])
or
rec_sum(num_arr.tap(&:shift))
The latter would [likely] be looking too cumbersome for the beginner, but it’s a very common rubyish approach: Object#tap yields the receiver to the block, returning the receiver. Inside a block (num_arr.tap(&:shift) is a shorthand for num_arr.tap { |a| a.shift } we mutate the array by shifting the element out, and it’s being returned as a result.
mudasobwa already explained why using shift doesn't give the expected result. Apart from that, your code is somehow unidiomatic.
In multi_gen you are creating an empty array and append elements to it using a for loop. You rarely have to populate an array manually. Instead, you can usually use one of Ruby's Array or Enumerable methods to generate the array. select is a very common one – it returns an array containing the elements for which the given block returns true:
(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]
In rec_sum, you check if num_arr == []. Although this works, you are creating an empty throw-away array. To determine whether an array is empty, you should call its empty?:
if num_arr.empty?
# ...
end
To get the remaining elements from the array, you use:
num_arr[1..num_arr.last]
which can be abbreviated by passing a negative index to []:
num_arr[1..-1]
There's also drop which might look a little nicer:
num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))
Another option to get first and remaining elements from an array is Ruby's array decomposition feature (note the *):
def rec_sum(num_arr)
if num_arr.empty?
0
else
first, *remaining = num_arr
first + rec_sum(remaining)
end
end
You could also consider using a guard clause to return from the method early:
def rec_sum(num_arr)
return 0 if num_arr.empty?
first, *remaining = num_arr
first + rec_sum(remaining)
end
Writing recursive methods is great for learning purposed, but Ruby also has a built-in sum method:
multi_gen.sum #=> 234168
or – since you are using an older Ruby version – inject:
multi_gen.inject(0, :+) #=> 234168
My problem is asking to iterate over an array of items and delete every other item until I reach the end of the array when I should start iterating backwards and keep deleting every other item and so on back and forth until only one item is left in the array.
for example: 1,2,3,4,5, would become 1,3,5 and then 3
I understand that I shouldn't be deleting from the original array so I created another array that just keeps every other item while decreasing its size but I can't get it to work and end up with an infinite loop.
arr=*(1..10)
s_arr=[]
until s_arr.length==1
i=0
while i<arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
s_arr=arr
end
Thank you.
Each iteration, replace the array with a version with every other element dropped
arr = *(1..10)
while arr.length > 1
arr = arr.select.with_index { |_, i| i % 2 == 0 }.reverse
end
This doesn't match your example, but your example seems inconsistent in the first place. If [1,2,3,4,5] goes to [1,3,5], then the next step should be [1,5] instead of [3].
This is similar to #Max's answer, but implemented recursively.
def last_one_standing(arr)
return arr.first if arr.size == 1
last_one_standing arr.select.with_index { |_,i| i.even? }.reverse
end
(1..16).each { |i| puts "1..%d: %d" % [i, last_one_standing((1..i).to_a)] }
1..1: 1
1..2: 1
1..3: 3
1..4: 3
1..5: 1
1..6: 1
1..7: 3
1..8: 3
1..9: 9
1..10: 9
1..11: 11
1..12: 11
1..13: 9
1..14: 9
1..15: 11
1..16: 11
try something like this so you can take advantage of Ruby's power
result = []
array.each_with_index {|val,idx| result << val if ( idx % 2 == 0)}
Try this on your ruby console:
arr=*(1..10)
x = (Math::log(arr.length) / Math::log(3)).to_i
3**x
arr=*(1..10)
s_arr=[]
until s_arr.length == 1
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
if s_arr.length != 1
s_arr=[]
end
end
Your problem was setting the s_arr array the same as the arr. Then you must add a conditional to tell it to stop when it does reach 1 otherwise it'll become 0 instead.
EDIT:
arr=*(1..10)
s_arr=[]
until s_arr.length == 1
s_arr = []
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
end
even better solution.
I have a simple ruby array and I want to select the element in the array just below the matching value.
numbers = [1,2,3,4,5,6,10]
The matching value I have is 10 but I want to be able to instead get the value before 10 which is 6.
numbers.some_magical_ruby_method {|n| n == 10} # I hope to return 6 since it's the element before 10
My question is what Ruby method exists for me to select the value before the matching value.
You could use Array#take with Array#index:
> numbers.take(numbers.index(10).to_i).last
=> 6
If a value is not found then the returned value is nil.
You can extend Array class with this method
class Array
def previous_element el
each_cons(2) {|prev, curr| return prev if curr == el }
end
end
numbers[numbers.index(10) - 1]
=> 6
result = nil
index = numbers.index(10)
if index and index > 0
result = numbers[(index - 1)]
end
result
# => 6
def number_before numbers, num
idx = numbers.index(num)
return numbers[idx - 1] unless (idx.nil? || idx == 0)
return nil
end
> numbers = [1,2,3,4,5,6,10]
> number_before numbers, 10 #=> 6
> numbers = [1,2,3]
> number_before numbers, 10 #=> nil
> numbers = [1,10,6]
> number_before numbers, 10 #=> 1
> numbers = [10,6,1]
> number_before numbers, 10 #=> nil
Find the index of 10, and returning the previous element, or nil if the number is not found or previous element is not found. The idx == 0 case is important, because the array index -1 will wrap around to the front in ruby.
def prev_nbr(numbers, nbr)
n = nil
enum = numbers.to_enum
loop do
return n if enum.peek == nbr
n = enum.next
end
nil
end
previous_nbr(numbers, 4) #=> 3
previous_nbr(numbers, 8) #=> nil
previous_nbr(numbers, 1) #=> nil
This answer gives the result as the value of the element at the index before the requested value:
numbers[((arr_ind=numbers.index(10)).to_i > 0) ? arr_ind-1 : numbers.length]
This could also be written as this (using nil instead of the numbers[numbers.length] result):
((arr_ind=numbers.index(10)).to_i > 0) ? numbers[arr_ind-1] : nil
Neither of these solutions suffer the problem of "wrapping" found when used with the simple nil.to_i solutions or when the requested value is at the beginning of the array, such as when the value to search is 0 or not found in the array. This solution avoids artificial looping and excess memory usage.
The only side effect is that the arr_ind variable is either created or overwritten if it already exists.
This short test demonstrates the results of searching for each of the numbers in the range (-1..13):
numbers = [1,2,3,4,5,6,10]
def answer(arr,element)
arr[((arr_ind=arr.index(element)).to_i > 0) ? arr_ind-1 : arr.length]
end
answers = [nil, nil, nil, 1, 2, 3, 4, 5, 6, nil, nil, nil]
(-1..13).each_with_index do |number, i|
puts "#{answer(numbers,number) == answers[i] ? 'Pass' : 'Fail'}: #{number}"
end
The output from this test shows:
Pass: -1
Pass: 0
Pass: 1
Pass: 2
Pass: 3
Pass: 4
Pass: 5
Pass: 6
Fail: 7
Pass: 8
Pass: 9
Fail: 10
Pass: 11
Pass: 12
Pass: 13
"Pass" means that the test result meets expectations and passes; "Fail" means that it did not meet expectations. Expected values are in the answers array, corresponding one-to-one with the values in the test range.
I am trying to write a method that takes an array and returns trueif there is an element that occurs three times in a row or false if it doesn't. I can't think of the syntax. Would you use count? See the example below.
def got_three?(array)
end
got_three?([1,2,2,3,4,4,4,5,6]) would return true as 4 shows up three times in a row.
Toying with the new Ruby 2.3.0 method chunk_while:
def got_three?(array)
array.chunk_while(&:==).any?{|g| g.size >= 3}
end
With Enumerable#chunk:
def got_three?(xs)
xs.chunk(&:itself).any? { |y, ys| ys.size >= 3 }
end
Not so smart but a naive one (using a instead of array since it is long):
a.each_index.any?{|i| a[i] == a[i + 1] and a[i + 1] == a[i + 2]}
I assume you don't have any nil in the array.
An alternative which may be more performant (as per #sawa's comments)...
def got_three?(array)
(0..(array.count-2)).any?{|i|array[i] == array[1+1] && array[i] == array[i+2]}
end
Look, ma, no indices!
def ducks_in_a_row?(arr, n)
cnt = 0
last = arr.first
arr.each do |d|
if d==last
cnt += 1
return true if cnt==n
else
last = d
cnt = 1
end
end
false
end
ducks_in_a_row?([1,2,3,4,5,6,6,7,7,7], 3)
#=> true
def got_three?(array)
array.each_cons(3).map{|g|g.uniq.length == 1}.any?
end
or as #wandmaker suggests...
def got_three?(array)
array.each_cons(3).any?{|g|g.uniq.length == 1}
end
Here is my take on this problem - I tried to make it generic, hopefully efficient so that the loop terminates as soon as n-consecutive elements are found.
def got_consecutive?(array, n = 3)
case array.size
when 0...n
return false
when n
return array.uniq.size == n
else
array[n..-1].each_with_object(array[0...n]) do |i, t|
(t.uniq.size == 1 ? (break t) : (t << i).shift)
end.uniq.size == 1
end
end
p got_consecutive?([])
#=> false
p got_consecutive?([1,2])
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3], 3)
#=> true
p got_consecutive?([1,2,2,3,2,3,3,3], 4)
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3,3,3,4,4,4,4], 5)
#=> true
The code takes care of border cases first such as when array did not have n elements in which case answer is obviously false, and another one being when the array had only n elements - in which case just a uniqueness check would suffice.
For cases where array size is greater than n, the code uses Enumerable#each_with_object with the initial object being an array of n elements from the array - this array is used also as temporary work area to track n consecutive elements and perform a check whether all those elements are same or not.
Given two arrays of equal size, how can I find the number of matching elements disregarding the position?
For example:
[0,0,5] and [0,5,5] would return a match of 2 since there is one 0 and one 5 in common;
[1,0,0,3] and [0,0,1,4] would return a match of 3 since there are two matches of 0 and one match of 1;
[1,2,2,3] and [1,2,3,4] would return a match of 3.
I tried a number of ideas, but they all tend to get rather gnarly and convoluted. I'm guessing there is some nice Ruby idiom, or perhaps a regex that would be an elegant answer to this solution.
You can accomplish it with count:
a.count{|e| index = b.index(e) and b.delete_at index }
Demonstration
or with inject:
a.inject(0){|count, e| count + ((index = b.index(e) and b.delete_at index) ? 1 : 0)}
Demonstration
or with select and length (or it's alias – size):
a.select{|e| (index = b.index(e) and b.delete_at index)}.size
Demonstration
Results:
a, b = [0,0,5], [0,5,5] output: => 2;
a, b = [1,2,2,3], [1,2,3,4] output: => 3;
a, b = [1,0,0,3], [0,0,1,4] output => 3.
(arr1 & arr2).map { |i| [arr1.count(i), arr2.count(i)].min }.inject(0, &:+)
Here (arr1 & arr2) return list of uniq values that both arrays contain, arr.count(i) counts the number of items i in the array.
Another use for the mighty (and much needed) Array#difference, which I defined in my answer here. This method is similar to Array#-. The difference between the two methods is illustrated in the following example:
a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1, 3, 2, 2]
For the present application:
def number_matches(a,b)
left_in_b = b
a.reduce(0) do |t,e|
if left_in_b.include?(e)
left_in_b = left_in_b.difference [e]
t+1
else
t
end
end
end
number_matches [0,0,5], [0,5,5] #=> 2
number_matches [1,0,0,3], [0,0,1,4] #=> 3
number_matches [1,0,0,3], [0,0,1,4] #=> 3
Using the multiset gem:
(Multiset.new(a) & Multiset.new(b)).size
Multiset is like Set, but allows duplicate values. & is the "set intersection" operator (return all things that are in both sets).
I don't think this is an ideal answer, because it's a bit complex, but...
def count(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end
def matches(a1, a2)
m = 0
a1_counts = count(a1)
a2_counts = count(a2)
a1_counts.each do |e, c|
m += [a1_counts, a2_counts].min
end
m
end
Basically, first write a method that creates a hash from an array of the number of times each element appears. Then, use those to sum up the smallest number of times each element appears in both arrays.