Is it necessary to refer to self in: yield(self[i]) - ruby

In this example from a blog post,
class Array
def each
i = 0
while(i < self.length) do
yield(self[i])
i += 1
end
end
end
my_array = ["a", "b", "c"]
my_array.each {|letter| puts letter }
# => "a"
# => "b"
# => "c"
Is it necessary to use self in the statement:
yield(self[i])
Or would it be ok to simply say:
yield i

Those are two entirely different things. If you do yield i you will actually yield the number i, which will cause the output to be 1 2 3. The point of the code however is to yield the elements of the array, so you yield self[i], which means "the ith element of the array self", or more technically "call the method [] on self with the argument i and yield the result".

yield(i) would yield a block for index, while yield(self[i]) would yield a block for ith element

Related

Finding the mode of an array in Ruby

When creating a method to find the mode of an array, I see people iterating over the array through a hash with default value 0:
def mode(array)
hash = Hash.new(0)
array.each do |i|
hash[i]+=1
end
end
or
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
Can someone explain the following part of the block?
hash[i] = hash[i] + 1 or h[v] = h[v] + 1
How does the iterator know to add +1 to each unique key of the hash? For example:
array = [1,1,1,2,3]
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
#=> {1:3, 2:1, 3:1}
If someone can explain how to find the mode of an array, I would be grateful.
In you first example, you need the method to return the hash that is created, or do some manipulation of the hash to compute the mode. Let's try it, just returning the hash (so I've added hash as the last line):
def hash_for_mode(array)
hash = Hash.new(0)
array.each do |i|
hash[i]+=1
end
hash
end
array = [1,3,1,4,3]
hash_for_mode(array) #=> {1=>2, 3=>2, 4=>1}
With hash_for_mode you can easily compute the mode.
By defining the hash h = Hash.new(0), we are telling Ruby that the default value is zero. By that, we mean that if a calculation is performed that depends on h[k] when k is not a key of h, h[k] will be set equal to the default value.
Consider, for example, when the first value of array (1 in my example) is passed into the block and assigned to the block variable i. hash does not have a key 1. (It has no keys yet.) hash[1] += 1 is shorthand for hash[1] = hash[1] + 1, so Ruby will replace hash[1] on the right side of the equality with the default value, zero, resulting in hash[1] => 1.
When the third value of array (another 1) is passed into the block, hash[1] already exists (and equals 1) so we just add one to give it a new value 2.
In case you were wondering, if we have:
hash = Hash.new(0)
hash[1] += 1
hash #=> {1=>1}
puts hash[2] #=> nil
hash #=> {1=>1}
That is, merely referencing a key that is not in the hash (here puts hash[2]), does not add a key-value pair to the hash.
Another common way to do the same thing is:
def hash_for_mode(array)
array.each_with_object({}) { |i,hash| hash[i] = (hash[i] || 0) + 1 }
end
hash_for_mode(array) #=> {1=>2, 3=>2, 4=>1}
This relies on the fact that:
(hash[i] || 0) #=> hash[i] if hash already has a key i
(hash[i] || 0) #=> 0 if hash does not have a key i, so hash[k]=>nil
(This requires that your hash does not contain any pairs k=>nil.)
Also, notice that rather than having the first statement:
hash = {}
and the last statement:
hash
I've used the method Enumerable#each_with_object, which returns the value of the hash. This is preferred here to using Enumerable#inject (a.k.a reduce) because you don't need to return hash to the iterator (no ; h needed).
array = [1,3,1,4,3]
array.group_by(&:itself).transform_values(&:count)
# => {1=>2, 3=>2, 4=>1}

Ruby - Return duplicates in an array using hashes, is this efficient?

I have solved the problem using normal loops and now using hashes, however I am not confident I used the hashes as well as I could have. Here is my code:
# 1-100 whats duplicated
def whats_duplicated?(array)
temp = Hash.new
output = Hash.new
# Write the input array numbers to a hash table and count them
array.each do |element|
if temp[element] >= 1
temp[element] += 1
else
temp[element] = 1
end
end
# Another hash, of only the numbers who appeared 2 or more times
temp.each do |hash, count|
if count > 1
output[hash] = count
end
end
# Return our sorted and formatted list as a string for screen
output.sort.inspect
end
### Main
# array_1 is an array 1-100 with duplicate numbers
array_1 = []
for i in 0..99
array_1[i] = i+1
end
# seed 10 random indexes which will likely be duplicates
for i in 0..9
array_1[rand(0..99)] = rand(1..100)
end
# print to screen the duplicated numbers & their count
puts whats_duplicated?(array_1)
My question is really what to improve? This is a learning excercise for myself, I am practising some of the typical brain-teasers you may get in an interview and while I can do this easily using loops, I want to learn an efficient use of hashes. I re-did the problem using hashes hoping for efficiency but looking at my code I think it isn't the best it could be. Thanks to anyone who takes an interest in this!
The easiest way to find duplicates in ruby, is to group the elements, and then count how many are in each group:
def whats_duplicated?(array)
array.group_by { |x| x }.select { |_, xs| xs.length > 1 }.keys
end
whats_duplicated?([1,2,3,3,4,5,3,2])
# => [2, 3]
def whats_duplicated?(array)
array.each_with_object(Hash.new(0)) { |val, hsh| hsh[val] += 1 }.select { |k,v| v > 1 }.keys
end
I would do it this way:
def duplicates(array)
counts = Hash.new { |h,k| h[k] = 0 }
array.each do |number|
counts[number] += 1
end
counts.select { |k,v| v > 1 }.keys
end
array = [1,2,3,4,4,5,6,6,7,8,8,9]
puts duplicates(array)
# => [4,6,8]
Some comments about your code: The block if temp[element] == 1 seems not correct. I think that will fail if a number occurs three or more times in the array. You should at least fix it to:
if temp[element] # check if element exists in hash
temp[element] += 1 # if it does increment
else
temp[element] = 1 # otherwise init hash at that position with `1`
end
Furthermore I recommend not to use the for x in foo syntax. Use foo.each do |x| instead. Hint: I like to ask in interviews about the difference between both versions.

Yield something when random block of calculations passed

The part I don't understand is how to make this yield so that the elements in the array have the calculation, (which is specified in the block), done to them if block_given?, prior to their being added together.
For example, the calculation could be |x| x+3 (to add 3 to each of the elements), but I want this to work for any sort of manipulation of the elements, such as |x| x**3 or |x| x*5, so that the elements inmy_ary ([1,2,3]) are changed as specified by the calculation.
So essentially I'm asking what I need to do to the part of my code that reads yield array.each{|x|} if block_given? . what I was trying to do here is say that each element in the array should have whatever is stated in the block done to the element, so that it is changed.
What I am passing to the this is something along the lines of my_ary.sum(2) {|x| x **4}.
class MyArray
attr_reader :ary
def init(ary)
#ary = ary
end
def sum(init_val = 0)
yield ary.each{|x|} if block_given?
(#ary.inject(0){|x,y|x+y}) + init_val
end
end
class MyArray
attr_reader :ary
def initialize(ary)
#ary = ary
end
def sum n, &block
new_ary = #ary.collect &block # ary after executing block
ary_sum = new_ary.inject(0){|sum, x| sum+=x} # sum all elements of the array
return ary_sum + n
end
end
def nsum n, &block, here & saves the block (code between {} or do; end) to instance of Proc. It's basically your block of code saved to variable.
#ary.collect &block here, collect want block not proc so & change proc to the block. collect execute block for each element, and return new array.
inject - yields element to the block, add it to sum, and it is returned as sum variable. On the next iteration (next yielding to the block) sum will be last value of previous iteration.
[1,2,3].inject(0){|s, x| s+=x}
# sum = 0; x = 1;
# sum = 1; x = 2
# sum = 3; x = 3
# sum = 6
# inject returns 6 because there is no elements in the array

Ruby: Overloading Yield Function

I noticed while learning Ruby that both of these uses of the each method work and produce the same output, and I was wondering how Ruby makes this happen (and how I can make it happen for my own functions):
my_array = [["hello","goodbye"],["picture","perfect"]]
my_array.each do |array|
puts array[0] + " " + array[1]
end
my_array.each do |first, second|
puts first + " " + second
end
My understanding is that when writing the definition of a method that accepts a code block, the yield method is utilized to pass arguments to the code block and call the block. But how can you utilize the yield method such that it passes different arguments depending on the provided code block? In the example case, it appears that the yield method passes the individual array elements when two parameters (i.e., first, second) are used within the block, and it passes the arrays themselves when one parameter is used within the block (i.e., array).
Neither each not yield are doing anything special here, that's just how block arguments work. Consider this simple example:
def f(x) yield x end
and now we can see what happens:
>> f([1,2]) { |a| puts a.inspect }
[1, 2]
>> f([1,2]) { |a, b| puts "#{a} - #{b}" }
1 - 2
>> f([1,2]) { |a, b, c| puts "#{a} - #{b} - #{c}" }
1 - 2 -
You'll see similar destructing in assignments:
a, b = [1, 2]
You can also do it explicitly with a splat:
a, b = *[1, 2]
or like this:
def g(x) yield *x end
g([1, 2]) { |a, b| puts "#{a} - #{b}" }
Presumably the block knows what sorts of things it will be given so the block is well positioned to unpack the arguments. Note that the g function has to know that its argument is splatable (i.e. an array) but f doesn't. f nicely puts the "what sort of thing is x" logic together in the call to f, g buries half of the logic inside itself. One place where the difference becomes apparent is when you use Enumerable methods on a Hash:
{ :where => :is, :pancakes => :house? }.map { |k, v| ... }
Enumerable#map doesn't need to know that a Hash works in key/value two element arrays, it just passes things around and leaves it up everyone else to worry about the details.

Ruby longest word in array

I built this method to find the longest word in an array, but I'm wondering if there's a better way to have done it. I'm pretty new to Ruby, and just did this as an exercise for learning the inject method.
It returns either the longest word in an array, or an array of the equal longest words.
class Array
def longest_word
# Convert array elements to strings in the event that they're not.
test_array = self.collect { |e| e.to_s }
test_array.inject() do |word, comparison|
if word.kind_of?(Array) then
if word[0].length == comparison.length then
word << comparison
else
word[0].length > comparison.length ? word : comparison
end
else
# If words are equal, they are pushed into an array
if word.length == comparison.length then
the_words = Array.new
the_words << word
the_words << comparison
else
word.length > comparison.length ? word : comparison
end
end
end
end
end
I would do
class Array
def longest_word
group_by(&:size).max.last
end
end
Ruby has a standard method for returning an element in a list with the maximum of a value.
anArray.max{|a, b| a.length <=> b.length}
or you can use the max_by method
anArray.max_by(&:length)
to get all the elements with the maximum length
max_length = anArray.max_by(&:length).length
all_with_max_length = anArray.find_all{|x| x.length = max_length}
Here's one using inject (doesn't work for an empty array):
words.inject(['']){|a,w|
case w.length <=> a.last.length
when -1
a
when 0
a << w
when 1
[w]
end
}
which can be shortened to
words.inject(['']){|a,w|
[a + [w], [w], a][w.length <=> a.last.length]
}
for those who like golf.
A two liner:
vc = ['asd','s','1234','1235'].sort{|a,b| b.size <=> a.size}
vc.delete_if{|a| a.size < vc.first.size}
#Output
["1235", "1234"]
or if you want use inject, this use your idea, but its more short.
test_array.inject{ |ret,word|
ret = [ret] unless ret.kind_of?(Array)
ret << word if word.size == ret.first.size
ret = [word] if word.size > ret.first.size
ret
}
module Enumerable
def longest_word
(strings = map(&:to_s)).
zip(strings.map(&:length)).
inject([[''],0]) {|(wws, ll), (w, l)|
case l <=> ll
when -1 then [wws, ll]
when 1 then [[w], l]
else [wws + [w], ll]
end
}.first
end
end
This method only depends on generic Enumerable methods, there's nothing Array specific about it, therefore we can pull it up into the Enumerable module, where it will also be available for Sets or Enumerators, not just Arrays.
This solution uses the inject method to accumulate the longest strings in an array, then picks the ones with the highest length.
animals = ["mouse", "cat", "bird", "bear", "moose"]
animals.inject(Hash.new{|h,k| h[k] = []}) { |acc, e| acc[e.size] << e; acc }.sort.last[1]
This returns:
["mouse", "mouse"]

Resources