Access hash values via database request-esque statements - ruby

Let's say I have two arrays, each containing any number of hashes with identical keys but differing values:
ArrayA = [{value: "abcd", value_length: 4, type: 0},{value: "abcdefgh", value_length: 8, type: 1}]
ArrayB = [{value: "ab", value_length: 2, type: 0},{value: "abc", value_length: 3, type: 1}]
Despite having any number, the number of hashes will always be equal.
How could I find the largest :value_length for every hash of a certain type?
For instance, the largest :value_length for a hash with a :type of 0 would be 4. The largest :value_length for a hash with a :type of 1 would be 8.
I just can't get my head around this problem.
It would be great if I could get the answer in the way I described above, in a database request-esque fashion.
Currently, I'm trying to do it like this:
# place all :value_length values in an array
flat_A = ArrayA.flatten.map{|h| h[:value_length]} #=> [4,8]
flat_B = ArrayB.flatten.map{|h| h[:value_length]} #=> [2,3]
But I don't know how I could compare the parallel results of separate arrays. i.e. (4 with 2, 8 with 3)
loop_A = 0
loop_B = 0
flat_A.each do |a|
flat_B each do |b|
if loop_A == loop_B
comparisson_array << a << b
#something like this I just can't think!!!!
comparisson_array.index comparisson_array.max #=> 1
end
loop_B += 1
end
loop_A += 1
end

Using Array#zip:
ArrayA = [{value_length: 4, type: 0},{value_length: 8, type: 1}]
ArrayB = [{value_length: 2, type: 0},{value_length: 3, type: 1}]
ArrayA.zip(ArrayB).map {
|a, b| [a[:value_length], b[:value_length]].max
} # => [4, 8]
NOTE: I assumed that ArrayA and ArrayB are sorted by type.

Related

How to sort by value in hash?

So I made a random number generator which is supposed to count the frequency of the numbers and display them in sorted order. I'm trying to use .sort but I can't figure out where to put it to sort the values of the hash in order. What I have so far:
MIN_VALUE = 1
count = 0
puts "Enter a number of random integers to generate"
resp = gets.to_i
p "number of integers generated is #{resp}"
puts "Now enter the maximum value the integers can be"
max_value = gets.to_i
p "max value is set to #{max_value}"
size = Array.new(resp)
while (count < resp)
int_value = (rand(MIN_VALUE..max_value))
size.push(int_value)
count = count + 1
end
puts size
freq = Hash.new(0)
size.each { |x| freq[x] += 1 }
freq.map{ |key, value| "#{key}x#{value}" }.join(',')
freq.each do |key,value|
puts "Frequency of #{key} is: #{value}"
end
Any help is greatly appreciated!
More or less the same soup, generating random numbers in an Integer#times loop:
upper_number = 10
sample_size = 100
freq = Hash.new(0) # initializing the hash with a default value of zero, for counting
sample_size.times { freq[rand((1..upper_number))] += 1 } # here the loop generating and counting
freq #=> {5=>13, 7=>10, 1=>11, 2=>13, 8=>13, 9=>6, 3=>6, 6=>9, 10=>11, 4=>8}
Then you can sort by frequencies (reverse order: -v) and by sample value (k), [-v, k]:
freq.sort_by{ |k, v| [-v, k] }.to_h #=> {2=>13, 5=>13, 8=>13, 1=>11, 10=>11, 7=>10, 6=>9, 4=>8, 3=>6, 9=>6} # for this run
freq.sum { |_, v| v} #=> 100 # of course
Suppose
arr = [4, 1, 3, 4, 2, 5, 1, 3, 4, 3, 4]
You can use the form of Hash::new that takes an argument, called its default value (which often, as here, is zero), to obtain the frequency of the elements of arr:
freq = arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
#=> {4=>4, 1=>2, 3=>3, 2=>1, 5=>1}
We see that
freq[1]
#=> 2
freq[99]
#=> 0
The second result follows because freq was defined to have a default value of 0. All that means is that if freq does not have a key k, freq[k] returns zero (and that does not alter freq).
Here are solutions to two possible interpretations of your question. Both use the method Enumerable#sort_by.
Sort the unique values of arr by decreasing frequency
freq.sort_by { |_,v| -v }.map(&:first)
#=> [4, 3, 1, 2, 5]
Sort the values of arr by decreasing frequency
arr.sort_by { |n| -freq[n] }
#=> [4, 4, 4, 4, 3, 3, 3, 1, 1, 2, 5]
Replace -v and -freq[n] with v and freq[n] to sort by increasing frequency.
I've used the local variable _ to represent the keys in the first interpretation to signify that it is not used in the block calculation. This is common practice.

Find hash in another array and substract in Ruby

I have two arrays of hashes that look like this:
a = [ { car_id: 1, motor_id: 1, quantity: 5 },
{ car_id: 1, motor_id: 2, quantity: 6 },
{ car_id: 5, motor_id: 3, quantity: 3 } ]
b = [ { car_id: 1, motor_id: 1, quantity: 2 },
{ car_id: 1, motor_id: 2, quantity: 3 } ]
I want to substract the quantities from each hash in b from the hashes in a for those hashes that have the same car_id & motor_id. So, my expected result would be:
c = [ {car_id: 1, motor_id: 1, quantity: 3},
{car_id: 1, motor_id: 2, quantity: 3},
{car_id: 5, motor_id: 3, quantity: 3 } ]
What is a good way to do this in Ruby? My thoughts were to iterate over a, and for each element find if there is any in b that have the same car_id and motor_id, if so, substract, and continue.
I suggest you first create a hash bqty for b that, for each element (hash) g of b, maps [g[:car_id], g[:motor_id]] into g[:quantity]:
bqty = b.each_with_object({}) {|g,h| h[[g[:car_id], g[:motor_id]]] = g[:quantity]}
#=> {[1, 1]=>2, [1, 2]=>3}
Next, map each element (hash) g of a to the desired hash. This is done by merging g into an empty hash h (or g.dup), then, if there is an element of bqty with the key key = [h[:car_id], h[:motor_id]], subtract bqty[key] from h[:quantity]. Note this leaves a and b unchanged.
a.map do |g|
{}.merge(g).tap do |h|
key = [h[:car_id], h[:motor_id]]
h[:quantity] -= bqty[key] if bqty.key?(key)
end
end
#=> [{:car_id=>1, :motor_id=>1, :quantity=>3},
# {:car_id=>1, :motor_id=>2, :quantity=>3},
# {:car_id=>5, :motor_id=>3, :quantity=>3}]
An alternative to the antepenultimate1 line is:
h[:quantity] -= bqty[key].to_i
since nil.to_i #=> 0.
1. How can one pass up an opportunity to use such a word?
This is basically what you suggested, except instead of an explicit check for car_id/motor_id it uses a hash instead.
ids = ->(h){h.values_at(:car_id, :motor_id)} # A function for extracting the key values
lookup = Hash[*a.flat_map{|h| [ids[h], h]}] # A map from ids -> hash w/ quantity
b.each{|h| lookup[ids[h]][:quantity] -= h[:quantity]} # Subtract each b from an a (will error if the a doesn't exist)
a == c # == true
Perhaps, you'd find the following more intuitive:
matched_result = []
while current_a = a.shift
if current_b = b.shift
if current_a[:car_id] == current_b[:car_id] && current_a[:motor_id] == current_b[:motor_id]
matched_result << {car_id: current_a[:car_id], motor_id: current_a[:motor_id], quantity: current_a[:quantity] - current_b[:quantity]}
end
else
matched_result << current_a
end
end
p matched_result

Set all values of a column in a multidimensional array

I have the following code that adds zero to values of a specific row in a multidimensional array:
def self.zero_row(matrix, row_index)
matrix[row_index].each_with_index do |item, index|
matrix[row_index][index] = 0
end
return matrix
end
I am wondering how I would go in order to make zeros all the values given a specific column_index.
def self.zero_column(matrix, col_index)
#..
end
To follow the same pattern as your other method you could do something like this:
def self.zero_column(matrix, col_index)
matrix.each_with_index do |item, row_index|
matrix[row_index][col_index] = 0
end
end
Would this fit the bill?
def self.zero_column(matrix, col_index)
matrix = matrix.transpose
matrix[col_index].map!{0}
matrix.transpose
end
Similarly, you could simplify your zero_row method
def self.zero_row(matrix, row_index)
matrix[row_index].map!{0}
matrix
end
If you need to deal with columns frequently, then I would say it is a design flaw to use a nested array. Nesting array has almost no benefit, and just makes things complicated. You should better have a flat array. It is much easier to manipulate columns equally as rows with flat arrays.
If you want a 3 by 2 matrix, then you can initialize it simply as an array with the length 3 * 2 like:
a = [1, 2, 3, 4, 5, 6]
Then, you can refer to the second column (index 1) or row as respectively:
a.select.with_index{|_, i| i % 2 == 1} # => [2, 4, 6]
a.select.with_index{|_, i| i / 2 == 1} # => [3, 4]
Rewriting all values of that column or row to 0 would be respectively:
a.each_index{|i| a[i] = 0 if i % 2 == 1} # => a: [1, 0, 3, 0, 5, 0]
or
a.each_index{|i| a[i] = 0 if i / 2 == 1} # => a: [1, 2, 0, 0, 5, 6]
Switching between an operation on a column and another on a row would be a matter of switching between % and /; you can see symmetry/consistency. If you need to keep the information regarding column length 2 within the array, then just assign it as an instance variable of that array.

Why does Ruby have zip and transpose when they do the same thing?

They seem to do the same thing.
g = [{ a: "A" }, { b: "B" }]
r = [{ x: "X" }, { y: "Y" }]
g.zip(r) # => [[{:a=>"A"}, {:x=>"X"}], [{:b=>"B"}, {:y=>"Y"}]]
[g,r].transpose # => [[{:a=>"A"}, {:x=>"X"}], [{:b=>"B"}, {:y=>"Y"}]]
Why have both methods?
#transpose Assumes that self is an array of arrays and transposes the rows and columns.
#zip assumes self can be any Enumerable object.
More differences are here
a = [12,11,21]
b = [1,2]
[a,b].transpose # transpose': element size differs (2 should be 3) (IndexError)
a.zip(b) # => [[12, 1], [11, 2], [21, nil]]
b.zip(a) # => [[1, 12], [2, 11]]
That to apply the #transpose method a and b should be of the same size. But for applying #zip, it is not needed b to be of the same size of a, ie b and a can be of any of size.
With #zip, the resultant array size will always be the size of self. With #transpose the resulting array size will be any of the inner array's size of self.

Iterating over hash of arrays

I have the following:
#products = {
2 => [
#<Review id: 9, answer01: 3, score: 67, style_id: 2, consumer_id: 2,
branch_id: 2, business_id: 2>
],
15 => [
#<Review id: 10, answer01: 3, score: 67, style_id: 2, consumer_id: 2,
branch_id: 2, business_id: 2>,
#<Review id: 11, answer01: 3, score: 67, style_id: 2, consumer_id: 2,
branch_id: 2, business_id: 2>
]
}
I want to average the scores for all reviews associated with each product's hash key. How can I do this?
To iterate over a hash:
hash = {}
hash.each_pair do |key,value|
#code
end
To iterate over an array:
arr=[]
arr.each do |x|
#code
end
So iterating over a hash of arrays (let's say we're iterating over each array in each point in the hash) would be done like so:
hash = {}
hash.each_pair do |key,val|
hash[key].each do |x|
#your code, for example adding into count and total inside program scope
end
end
Yes, just use map to make and array of the scores for each product and then take the average of the array.
average_scores = {}
#products.each_pair do |key, product|
scores = product.map{ |p| p.score }
sum = scores.inject(:+) # If you are using rails, you can also use scores.sum
average = sum.to_f / scores.size
average_scores[key] = average
end
Thanks for the answer Shingetsu, I will certainly upvote it. I accidentally figured the answer out myself.
trimmed_hash = #products.sort.map{|k, v| [k, v.map{|a| a.score}]}
trimmed_hash.map{|k, v| [k, v.inject(:+).to_f/v.length]}

Resources