ways to call self-written ruby methods - ruby

How do I write a ruby method that may be called by appending the method name to an object?
i.e. get the quarter period of a specified date
def quarter(dateObject)
quarters = { 1 => 1, 2 => 1, 3 => 1, 4 => 2, 5 => 2, 6 => 2, 7 => 3, 8 => 3, 9 => 3, 10 => 4, 11 => 4, 12 => 4 }
quarters[dateObject.month]
end
I can use this method now like this:
quarter(Date.today)
but how do I manage to use it like this:
Date.today.quarter
or, even better, in both ways?

You can patch it into the class. Be wary of what you are doing and make sure the method doesn't already exist when modifying a class.
class Date
def quarter
quarters = { 1 => 1, 2 => 1, 3 => 1, 4 => 2, 5 => 2, 6 => 2, 7 => 3, 8 => 3, 9 => 3, 10 => 4, 11 => 4, 12 => 4 }
quarters[self.month] #self is your instance of the Date object
end
end

Related

Array#each not performing as expected with nested while loop

The method below randomly generates the letters for the 6 sides of each of 16 dice meant to be used for Boggle based off the letter_frequencies hash. When the code is run, it iterates through the first element of boggle_dice, an empty array, filling it with random letters.
However, it proceeds to use that first iteration to fill the other 15 elements in boggle_dice with the same letters it has randomly chosen for the first element.
What is causing this to happen, and how should the iterative process be altered to work as intended?
At the end of iteration, each of the keys should appear in boggle_dice the number of times equal to the value associated with it in the hash.
def create_board
letter_frequencies = {
'e' => 10,
'a' => 8,
'i' => 7,
'o' => 6,
'l' => 5,
'n' => 5,
's' => 5,
't' => 5,
'd' => 4,
'r' => 4,
'u' => 4,
'b' => 3,
'c' => 3,
'g' => 3,
'h' => 3,
'm' => 3,
'p' => 3,
'y' => 3,
'f' => 2,
'k' => 2,
'v' => 2,
'w' => 2,
'j' => 1,
'q' => 1,
'x' => 1,
'z' => 1
}
boggle_dice = Array.new(16, [])
boggle_dice.each do |die|
while die.length < 6 do
random_letter = letter_frequencies.keys.sample
letter_frequencies[random_letter] <=0 ? next : die << random_letter
letter_frequencies[random_letter] -= 1
end
end
end
That is a common beginner's mistake. It is because you did Array.new(16, []), the same array is modified repeatedly. It can be fixed by changing it to Array.new(16){[]}.

Add item to hash went unexpectedly?

EDIT: My bad!!!Problem solved (I tested this on ruby 1.8, worked as expected on ruby 1.9)
I have an existing hash and wanted to sort it such that all the keys will be in numerical order.
a = {4 => 5, 8 => 20, 3 => 2, 6 => 1, 7 => 10, 2 => 1 }
=> #Wanted Newhash = {2 => 1, 3 => 2, 4 => 5, 6 => 1, 7 => 10, 8 => 20 }
Here is what I did:
b = a.keys.sort => [2,3,4,6,7,8]
c ={}
for key in b
p key
c[key] = a[key]
p c
end
Here is the output:
2
{2=>1}
3
{2=>1, 3=>2}
4
{2=>1, 3=>2, 4=>5}
6
{6=>1, 2=>1, 3=>2, 4=>5}
7
{6=>1, 7=>10, 2=>1, 3=>2, 4=>5}
8
{6=>1, 7=>10, 2=>1, 8=>20, 3=>2, 4=>5}
The thing I don't understand is:
The key I sorted in b is in order that I wanted. I supposed if I added it to a new hash it would be added to the end of the hash but it wasn't the case here. How so? The key 6 with its value got added in the front and the key 7 got added after that and then key 8 with its value was inserted in between key 2 and 3. Any explanation?
Probably, you are using Ruby < 1.9. That is the reason you did not get the order you wanted.

Accessing Array of Hashes

I am trying to access a value from an array of hash. An example array looks like this:
family = [
[
{ "Homer" => 1, "Marge" => 2, "Lisa" => 3, "Maggie" => 4,
"Abe" => 5, "Santa's Little Helper" => 6
}
],
[
{ "Homer" => 2, "Marge" => 4, "Lisa" => 6,
"Maggie" => 8, "Abe" => 10, "Santa's Little Helper" => 12
}
]
]
If I try to access the hash value for key "Homer" in array indexed 0 (family[0]) using the statement below and hoping to get the value 1:
family[0]["Homer"]
I get an error which says
"test.rb:4:in `[]': can't convert String into Integer (TypeError)"
Any suggestions on how one might be able to access a hash value in such an array, in a simple statement?
You should try family[0][0]["Homer"].
In your case family[0] gives you :
[{ "Homer" => 1, "Marge" => 2, "Lisa" => 3, "Maggie" => 4, "Abe" => 5,"Santa Little Helper" => 6}]
which is an array. The hash you want is inside it and can be got with family[0][0] :
{ "Homer" => 1, "Marge" => 2, "Lisa" => 3, "Maggie" => 4, "Abe" => 5,"Santa Little Helper" => 6}
So you can now use family[0][0]["Homer"] which will give you the value 1.
The array indices are always numeric values. If you get a can't convert String into Integer (TypeError) error message an exception is thrown because you are trying to access array element using a string that can't be converted to an integer.
You don't actually have an array of hashes. You have an array of arrays of hashes.
Your error occurs because you dereference your structure with [0] which gives you the first array of hashes, now you try to access the key 'homer' which doesnt exist because arrays are keyed by integers.
Here is an example of how you could see all of the values, see if you can get 'homer' on your own:
family.each do |a| # this is an array of arrays of hashes
a.each do |h| # this is an array of hashes
h.each do |k,v| # this is a hash
puts "#{k} => #{v}"
end
end
end
Output:
Homer => 1
Marge => 2
Lisa => 3
Maggie => 4
Abe => 5
Santa's Little Helper => 6
Homer => 2
Marge => 4
Lisa => 6
Maggie => 8
Abe => 10
Santa's Little Helper => 12
#Arup Rakshit is absolutely correct about how to get your value. But you should also know that you don't have an array of hashes, you have an array of arrays, and those sub-arrays contain hashes. Based on your title I'm concluding that you probably want a structure more like
family = [
{ "Homer" => 1, "Marge" => 2, "Lisa" => 3, "Maggie" => 4,
"Abe" => 5, "Santa's Little Helper" => 6
},
{ "Homer" => 2, "Marge" => 4, "Lisa" => 6,
"Maggie" => 8, "Abe" => 10, "Santa's Little Helper" => 12
}
]

Ruby way of summing up dictionary values

I have something like this:
a = [{"group_id" => 1, "student_id" => 3, "candies" => 4},
{"group_id" => 2, "student_id" => 1, "candies" => 3},
{"group_id" => 1, "student_id" => 2, "candies" => 2},
{"group_id" => 3, "student_id" => 4, "candies" => 6},
{"group_id" => 1, "student_id" => 5, "candies" => 1},
{"group_id" => 3, "student_id" => 6, "candies" => 1},
{"group_id" => 4, "student_id" => 8, "candies" => 3}]
I have three groups of students and each student gets a certain number of candies. I wish to count the total number of candies in a group. For that, I need to know the students which belong to a certain group and accumulate their candy count. I can do it using loops and initializing counts to zero:
aa = a.group_by { |a| a["group_id"] }
# =>
{
1 => [
{"group_id"=>1, "student_id"=>3, "candies"=>4},
{"group_id"=>1, "student_id"=>2, "candies"=>2},
{"group_id"=>1, "student_id"=>5, "candies"=>1}
],
2 => [{"group_id"=>2, "student_id"=>1, "candies"=>3}],
3 => [
{"group_id"=>3, "student_id"=>4, "candies"=>6},
{"group_id"=>3, "student_id"=>6, "candies"=>1}
],
4 => [{"group_id"=>4, "student_id"=>8, "candies"=>3}]
}
But I'm not able to accumulate the values within the group_id. I wonder if there are any succinct ways of representing it. How do I sum the total number of candies that are present in a group?
The first your step (grouping) is correct. After that you can use the following:
a.group_by {|g| g['group_id']}.map do |g, students|
{group_id:g, candies:students.map {|st| st['candies']}.inject(&:+)}
end
map function is often used with collections instead of loops to make some operation on each element and return modified version of the collection.
Output:
[{:group_id=>1, :candies=>7},
{:group_id=>2, :candies=>3},
{:group_id=>3, :candies=>7},
{:group_id=>4, :candies=>3}]
Adding to #StasS answer, a more direct hash way to do (with a more cryptic code) is like this:
> Hash[a.group_by{|g| g['group_id']}.map{|g,s| [g, s.inject(0){|a,b| a + b["candies"]}]}]
=> {1=>7, 2=>3, 3=>7, 4=>3}
you can unfold the line like this:
groups = a.group_by{|g| g['group_id']}
id_candies_pairs = groups.map{|g,s| [g, s.inject(0){|a,b| a + b["candies"]}]}
id_candies_hash = Hash[id_candies_pairs]
return id_candies_hash
Riffing on the answer by #StasS, you can also just build a simpler looking hash like:
totals_by_group_id = {}
a.group_by {|g| g['group_id']}.map do |g, students|
totals_by_group_id[g] = students.map {|st| st['candies']}.inject(&:+)
end
The resulting totals_by_group_id hash is:
{1=>7, 2=>3, 3=>7, 4=>3}

How to get the last element of an array in Ruby?

Example:
a = [1, 3, 4, 5]
b = [2, 3, 1, 5, 6]
How do I get the last value 5 in array a or last value 6 in array b without using a[3] and b[4]?
Use -1 index (negative indices count backward from the end of the array):
a[-1] # => 5
b[-1] # => 6
or Array#last method:
a.last # => 5
b.last # => 6
One other way, using the splat operator:
*a, last = [1, 3, 4, 5]
a => [1, 3, 4]
last => 5

Resources