Ruby refactoring of a method - ruby

Would love to refactor this into just one line:
def sum_something
sum = 0
self.each { |a| sum += a }
return sum
end
There must be a way to define 'sum' within the block and I could drop the 'return'.

def sum_something
inject(0, :+)
end

Related

Sum values recursively in one line

I am pretty sure that it can be done in one line using things like map, sum etc. I cannot figure out how exactly, because I just started learning ruby. Could someone help? Thanks
class Something < ApplicationRecord
def function
res = items.count
items.each do |i|
res += i.function
end
res
end
I'm not sure why you need to do it recursively and in one line, but you can try something like this:
edit:
def add(arr)
return 0 if arr.length == 0
# if the arr argument is an empty array, return 0.
arr[0] + add(arr[1..-1])
# add the first element of the array to the result of calling add
# on the array minus the first element.
end
If you just want to sum an array as concisely as possible, all you need to do is [1, 2, 3].sum or [1,2,3,4].reduce(&:+). No recursion needed.
The straightforward oneliner equivalent to yours:
def function
items.count + items.sum(&:function)
end
Demo (testing it alongside your original):
class Something
attr_accessor :items
def initialize(items = [])
self.items = items
end
def function
res = items.count
items.each do |i|
res += i.function
end
res
end
def function2
items.count + items.sum(&:function2)
end
end
root = Something.new([
Something.new,
Something.new([
Something.new,
Something.new([
Something.new,
Something.new([
Something.new
])
])
])
])
puts root.function
puts root.function2
Prints:
7
7
Another way:
def function
items.sum { |i| 1 + i.function }
end
By the way, you count all items except for the root item. Is that intentional?
You could count all including the root with this:
def function
1 + items.sum(&:function)
end
Not in one line but this is how you can do this recursively.
def add_array(arr)
return arr.first if arr.length == 1
return nil if arr.length < 1
arr.pop + add_arr(arr)
end

How can I modify my ruby method so it takes in a block of code as well?

I have a method called myFilter that takes in an array, and filters out the elements that don't meet the requirement.
For example.
arr = [4,5,8,9,1,3,6]
answer = myfilter(arr) {|i| i>=5}
this run would return an array with elements 5,8,9,6 since they are all greater than or equal to 5.
How would I preform this? the algorithm is easy, but I don't understand how we take in that condition.
Thank you.
I take for granted you don't want to use select method or similar but you want to understand how blocks work.
def my_filter(arr)
if block_given?
result = []
arr.each { |element| result.push(element) if yield element } # here you use the block passed to this method and execute it with the current element using yield
result
else
arr
end
end
The idiomatic way would be:
def my_filter(arr)
return enum_for(:my_filter, arr) unless block_given?
arr.each_with_object([]) do |e, acc|
acc << e if yield e
end
end
More info on Enumerator::Lazy#enum_for.
you can do
def my_filter(arr, &block)
arr.select(&block)
end
then call
my_filter([1, 2, 3]) { |e| e > 2 }
=> [3]
but instead you can just call select with a block directly :)

How to create a custom implementation of inject method?

It looks pretty easy at first. Here is the first code block that I wrote in a few minutes:
module Enumerable
def my_inject(memo=false)
memo = memo ? memo : self[0]
self.my_each do |item|
memo = yield(memo, item)
end
return memo
end
end
When the inject method is called with an initial value, it works as expected. But I haven't been able to find a way to make it properly work without any initial value.
For example:
puts [1,2,3,4].my_inject(0) { |memo, i| memo+i} #=> 10
puts [1,2,3,4].my_inject { |memo, i| memo+i} #=> 11
And this is what original Ruby inject method outputs:
puts [1,2,3,4].inject { |memo, i| memo+i} #=> 10
The Ruby docs says
If you do not explicitly specify an initial value for memo, then the
first element of collection is used as the initial value of memo.
So how to set proper data type for initial value? Should we have a condition for every data type in Ruby and set a default value for each of data types?
If you debug Ruby's own implementation you will see that it starts the iteration from the second element in case no default memo is given:
> [1,2,3].inject { |m, i| puts "M: #{m} | I: #{i}"; break }
M: 1 | I: 2
Therefore your implementation should look like this:
def my_inject(*args)
init = args.size > 0
memo = init ? args[0] : self[0]
self.drop(init ? 0 : 1).my_each do |item|
memo = yield(memo, item)
end
return memo
end
puts [1,2,3,4].my_inject(0) { |memo, i| memo+i} #=> 10
puts [1,2,3,4].my_inject { |memo, i| memo+i} #=> 10
The reason for init = args.size > 0 is you have to take into account that someone might want to do [...].my_inject(false) { ... }. Therefore you cannot check if memo == false to determine if you need to skip the first element or not, you have to check the actual argument count that was given.
So how to set proper data type for initial value? Should we have a condition for every data type in Ruby and set a default value for each of data types?
No, the default value is the first element of the collection.
Ruby allows you to define your own classes, so writing a case for every possible type is impossible anyway, there are infinitely many of them.
Just out of curiosity—tail-recursive solution:
class Array
def my_inject(memo = nil, enum = self.each)
return enum_for(:my_inject) unless block_given?
memo = enum.next if memo.nil?
my_inject(yield(memo, enum.next), enum, &Proc.new)
rescue StopIteration
memo
end
end
[1,2,3].my_inject(3) { |memo, v| memo += v }
#⇒ 9
[1,2,3].my_inject &:*
#⇒ 6

Make an array in Ruby

I am very beginner in Ruby and probably the question is too easy but well, I've already spent some time on it and couldn't find a solution.
My Ruby script takes a number (ex 10) and a name (ex Vincent). What I want is to make an array looking like
Vincent0
Vincent1..
Vincent9
I can't figure a way to make it..
def arrayfy(string, number)
arr = []
0.upto(number-1) do |i|
arr << "#{string}#{i}"
end
return arr
end
Update: To add these as variables to the class
class Foo
def arrayfy(string, number)
0.upto(number-1) do |i|
var_string = "##{string}#{i}"
var_symbol = var_string.to_sym
self.instance_variable_set(var_symbol, "")
end
end
end
Array.new(10) {|i| "Vincent#{i}"}
gives you
["Vincent0", "Vincent1", "Vincent2", "Vincent3", "Vincent4", "Vincent5",
"Vincent6", "Vincent7", "Vincent8", "Vincent9"]
The documentation for Array is available at http://www.ruby-doc.org/core/classes/Array.html (googling for Array RDoc will give you the URL).
The bit in the braces ({|i| "Vincent#{i}"}) is called a block. You'll definitely want to learn about them.
Using Array.new with a block (docs):
def create_array(count, name)
Array.new(10) { |i| "#{name}#{i} }
end
Using Enumerable#reduce (docs):
def create_array(count, name)
(0...count).reduce([]) { |m,i| m << "#{name}#{i}" }
end
Or using Enumerable#each_with_object (docs):
def create_array(count, name)
(0...count).each_with_object([]) { |i,a| a << "#{name}#{i}" }
end
Using it:
# Using the array (assigning to variables)
array = create_array(10, 'Vincent') # => ['Vincent0', 'Vincent1', 'Vincent2' ...]
name = array[1] # => 'Vincent1'
Just for the record, a solution in a more functional style:
>> def arrayify(str, n)
.. ([str] * n).zip(0...n).map(&:join)
.. end
#=> nil
>> arrayify('Vincent', 10)
#=> ["Vincent0", "Vincent1", "Vincent2", "Vincent3", "Vincent4", "Vincent5", "Vincent6", "Vincent7", "Vincent8", "Vincent9"]
def array_maker(number, string)
result = []
for i in 0..number do
result << "#{string}#{i}"
end
result
end

difficulty modifying two dimensional ruby array

Excuse the newbie question. I'm trying to create a two dimensional array in ruby, and initialise all its values to 1. My code is creating the two dimensional array just fine, but fails to modify any of its values.
Can anyone explain what I'm doing wrong?
def mda(width,height)
#make a two dimensional array
a = Array.new(width)
a.map! { Array.new(height) }
#init all its values to 1
a.each do |row|
row.each do |column|
column = 1
end
end
return a
end
It the line row.each do |column| the variable column is the copy of the value in row. You can't edit its value in such way. You must do:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.each do |row|
row.map!{1}
end
return a
end
Or better:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
a = Array.new(width){ Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
Array.new(width) { Array.new(height){1} }
end
each passes into the block parameter the value of each element, not the element itself, so column = 1 doesn't actually modify the array.
You can do this in one step, though - see the API docs for details on the various forms of Array#new. Try a = Array.new(width) {|i| Array.new(height) {|j| 1 } }
you can create it like this?
a=Array.new(width) { Array.new(height,1) }
column in your nested each loop is a copy of the value at that place in the array, not a pointer/reference to it, so when you change its value you're only changing the value of the copy (which ceases to exist outside the block).
If you just want a two-dimensional array populated with 1s something as simple as this will work:
def mda(width,height)
[ [1] * width ] * height
end
Pretty simple.
By the way, if you want to know how to modify the elements of a two-dimensional array as you're iterating over it, here's one way (starting from line 6 in your code):
#init all its values to 1
a.length.times do |i|
a[i].length.times do |j|
a[i][j] = 1
end
end

Resources