Add item to hash went unexpectedly? - ruby

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.

Related

How do I find the keys and the value in a hash with the minimum value, only among certain keys?

Using Ruby 2.4. I have a hash with keys and values that are both numbers (integers). Given a set of keys, how do I find the entry (both key and value) that has the lowest value? If I wanted to find the minimum value I could do
my_hash.select{|k, v| my_selected_keys.include?(k) }.values.min_by(&:last)
But this only gets me the value, not both the key and the value. Also note that values in my hash are not necessarily unique.
Is this what you're looking for?
It gets all keys that have the minimum value. I've split out and named many of the intermediate objects for clarity:
hash = { 4 => 5, 2 => 9, 3 => 1, 8 => 5 }
selected = Set[2, 4, 8]
hash_subset = hash.slice(*selected)
keys_by_value = hash_subset.group_by(&:last).each_value { |group| group.map!(&:first) }
min_value, keys_with_min_val = keys_by_value.min_by(&:first)
# => [5, [4, 8]]
and the obligatory ruby oneliner:
hash.slice(*selected).group_by(&:pop).each_value(&:flatten!).min_by(&:first)
You can map the selected keys sel_keys:
hash = {1 => 2, 3 => 1, 4 => 5, 5 => 1}
sel_keys = [3, 4]
sel_keys.map { |k| [k,hash[k]] }.min_by(&:last) #=> [3, 1]
Caveat: This returns only the first key found with the min value.
You could sort the hash by the value and then take the first element
my_hash.select{|k, _| my_selected_keys.include?(k) }.sort_by{|_, v| v}.first
Here is an alternative solution
hash.reduce([nil, Float::INFINITY]) { |acc, pair| acc[1] < pair[1] ? acc : pair }

How to instantiate a multilevel hash with an arbitrary depth

I want to create a hash from the result of a database query such that its keys are the column values except the last one, and the value are the last column value (or a default value). For example, if I have rows:
1 2 3 1
1 2 4 9
1 3 2 nil
and a default value of 111, I should get:
{
1 =>
{
2 => { 3 => 1, 4 => 9},
3 => { 2 => 111}
}
}
I want to make the method generic enough to handle an arbitrary number of columns, so the signature could be:
to_lookup(rows, default_value, value_column, *columns)
How would I go about that?
Update: forgot a comma in the output.
[Edit: after reading #cthulhu's answer, I think I may have misinterpreted the question. I assumed that consecutive rows were to be grouped, rather than all rows to be grouped. I will leave my answer for the former interpretation.]
I believe this is what you are looking for:
def hashify(arr)
return arr.first.first if arr.first.size == 1
arr.slice_when { |f,s| f.first != s.first }.
each_with_object({}) do |a,h|
key, *rest = a.transpose
h[key.first] = hashify(rest.transpose)
end
end
hashify [[1, 2, 3, 1], [1, 2, 4, 9], [1, 3, 2, nil]]
#=> {1=>{2=>{3=>1, 4=>9}, 3=>{2=>nil}}}
hashify [[1, 2, 3, 1], [1, 2, 4, 9], [2, 3, 2, nil]]
#=> {1=>{2=>{3=>1, 4=>9}}, 2=>{3=>{2=>nil}}}
Replacing nil with the default can be done before or after the construction of the hash.
Enumerable#slice_when was bestowed upon us in v2.2. For earlier versions, you could replace:
arr.slice_when { |f,s| f.first != s.first }
with
arr.chunk { |row| row.first }.map(&:last)
I simplified things by removing the ability to pass a default,
I also simplified the signature method to have only one parameter.
RSpec.describe "#to_lookup" do
def to_lookup(rows)
return rows.first.first if rows.flatten.size == 1
h = {}
rows.group_by { |e| e.first }.each_entry do |k, v|
v.each &:shift
h[k] = to_lookup(v)
end
h
end
let :input do
[
[1, 2, 3, 1],
[1, 2, 4, 9],
[1, 3, 2, 111],
]
end
let :output do
{
1 => {
2 => {3 => 1, 4 => 9},
3 => {2 => 111}
}
}
end
it { expect(to_lookup(input)).to eq(output) }
end
BTW I wonder what output do you want for following input:
1 2 3 1
1 2 3 2
EDIT: working code snippet: http://rubysandbox.com/#/snippet/566aefa80195f1000c000000

Ruby Arrays - Find the sums of the diagonals

Haven't seen this one before, but I was wondering how you can find the sums of both diagonals of a 2D array in Ruby. Say you have a simple array, with 3 rows and 3 columns.
array = [1,2,3,4,5,6,7,8,9]
I can break it into groups of three by using
array.each_slice(3).to_a
Would now be
[1,2,3], [4,5,6], [7,8,9]
[1,2,3]
[4,5,6]
[7,8,9]
In this case, the diagonals are
1 + 5 + 9 = 15
3 + 5 + 7 = 15
So the total sum would be 15 + 15 = 30
I was thinking I could do something like
diagonal_sum = 0
for i in 0..2
for j in 0..2
diagonal_sum += array[i][j]
end
end
Here is my try :
array = [1,2,3,4,5,6,7,8,9]
sliced = array.each_slice(3).to_a
# As sliced size is 3, I took 2, i.e. 3 - 1
(0..2).map { |i| sliced[i][i] } #=> [1, 5, 9]
(0..2).map { |i| sliced[i][-i-1] } # => [3, 5, 7]
(0..2).map { |i| sliced[i][i] }.reduce :+
# => 15
(0..2).map { |i| sliced[i][-i-1] }.reduce :+
# => 15
As per the above observation it seems in one iteration you can do solve :
left_diagonal, right_diagoal = (0..2).each_with_object([[], []]) do |i, a|
a[0] << sliced[i][i]
a[1] << sliced[i][-i-1]
end
left_diagonal.reduce(:+) # => 15
right_diagonal.reduce(:+) # => 15
Added, OOP style of code :
class SquareMatrix
attr_reader :array, :order
def initialize array, n
#array = array.each_slice(n).to_a
#order = n
end
def collect_both_diagonal_elements
(0...order).collect_concat { |i| [ array[i][i], array[i][-i-1] ] }
end
def collect_left_diagonal_elements
(0...order).collect { |i| array[i][i] }
end
def collect_right_diagonal_elements
(0...order).collect { |i| array[i][-i-1] }
end
def sum_of_diagonal_elements type
case type
when :all then collect_both_diagonal_elements.reduce(0, :+)
when :right then collect_right_diagonal_elements.reduce(0, :+)
when :left then collect_left_diagonal_elements.reduce(0, :+)
end
end
end
array = [1,2,3,4,5,6,7,8,9]
sqm = SquareMatrix.new array, 3
sqm.collect_both_diagonal_elements # => [1, 3, 5, 5, 9, 7]
sqm.sum_of_diagonal_elements :all # => 30
sqm.collect_left_diagonal_elements # => [1, 5, 9]
sqm.sum_of_diagonal_elements :left # => 15
sqm.collect_right_diagonal_elements # => [3, 5, 7]
sqm.sum_of_diagonal_elements :right # => 15
The following is mostly for the academic discussion:
For the main diagonal, you are looking for the "Trace" function which is defined for the "Matrix" class. So the following will work (although it doesn't get you the other diagonal and I wouldn't bet on its efficiency):
require 'Matrix'
a = array.each_slice(3).to_a
Matrix[*a].trace
To get the other diagonal you have to somehow "flip" the matrix, so the following seems to work (Since the result of each_slice is an array of rows, reverse reverses the order of the row. Reversing the order of the columns is more difficult):
Matrix[*a.reverse].trace
I totally forgot about #map.with_index ...Thanks to #xlembouras , heres a one-liner
first_diagonal = array.map.with_index {|row, i| row[i]} .inject :+
inverted_diagonal = array.map.with_index {|row, i| row[-i-1]} .inject :+
It's possible to make it a one-liner:
first_diagonal, inverted_diagonal = (array.map.with_index {|row, i| row[i]} .inject :+) , (array.map.with_index {|row, i| row[-i-1]} .inject :+)
Original:
Here's a thought, which makes me think it would be great to have a #map_with_index method:
for a first to last diagonal:
i = -1
array.map { |row| row[i=i+1] }.inject :+
for the last to first diagonal (assuming a square array):
i = array.length
array.map { |row| row[i=i-1] }.inject :+
a = [1,2,3,4,5,6,7,8,9]
p a.values_at(0,2,4,4,6,8).inject(&:+) #=> 30
I would try iterating through the array and keep the values that I need according to the length of the (grouped) array
array = [[1,2,3], [4,5,6], [7,8,9]]
dimension = array.length
array.flatten.map.with_index do |x,i|
x if [0, dimension - 1].include?(i % dimension)
end.compact.inject(:+)
#=> 30
You don't need to first apply slice:
arr = [1,2,3,4,5,6,7,8,9]
We visualize arr as:
1 2 3
4 5 6
7 8 9
n = Math.sqrt(arr.size).round
#=> 3
For the main diagonal:
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] }
#=> 15
For the off-diagonal:
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 15
Another example:
arr = [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]
1 2 3 4
5 6 7 8
9 0 1 2
3 4 5 6
n = Math.sqrt(arr.size).round
#=> 4
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] } +
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 14 + 14 => 28
require 'Matrix'
arr = [[1, 3, 4], [2, 5, 7], [6, 7, 8]]
diag1 = Matrix[*arr].tr
diag2 = Matrix[*arr.reverse].tr
def diagonal(array)
single=array.flatten
new=[]
i=array.length-1
while i < single.length-2
new << single[i]
i+=array.length-1
end
new.sum
end
p diagonal([
[1, 2, 3],
[4, 5, 6],
[7, 9, 8],
])
OUTPUT
15
That is for finding the sum of right diagonal of a 2D array

ways to call self-written ruby methods

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

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