Count keys inside of hash that have specific value - ruby

I have a Hash that looks like:
day = {
:morning => true,
:afternoon => false,
:evening => true,
:night => true
}
I'd like to get the percent of values that == true
I'm current doing
sum = day.count.to_f
adherence_percent = (day.select{|k, v| v == true }.count / sum) * 100
:> 66.6666666
Is there a more elegant/efficient way I can do this?

You can do this using count and last
day.count(&:last).fdiv(day.size)
Demonstration

If you know there are only true/false values in there
day.values.count(&:itself)
otherwise
day.values.count { |item| item == true }
usage:
sum = day.count.to_f
adherence_percent = day.values.count(&:itself) / sum * 100

Related

How to sum arrays based on the first field

I have 3 array of hashes:
a = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]
b = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]
c = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]
and I have to merge them into one, based on the name prop of each identifier, so the result will be:
d = [{name: 'Identifier', value: 1500 }, {name: 'Identifier2', value: 150}]
Is there a smart ruby way of doing this, or do I have to do create another hash where the keys are the identifiers, the values the values and then transform it into an array?
Thank you.
When the values of a single key in a collection of hashes are to be totaled I usually begin by constructing a counting hash:
h = (a+b+c).each_with_object({}) do |g,h|
h[g[:name]] = (h[g[:name]] || 0) + g[:value]
end
#=> {"Identifier"=>1500, "Identifier2"=>150}
Note that if h does not have a key g[:name], h[g[:name]] #=> nil, so:
h[g[:name]] = (h[g[:name]] || 0) + g[:value]
= (nil || 0) + g[:value]
= 0 + g[:value]
= g[:value]
We may now easily obtain the desired result:
h.map { |(name,value)| { name: name, value: value } }
#=> [{:name=>"Identifier", :value=>1500},
# {:name=>"Identifier2", :value=>150}]
If desired these two expressions can be chained:
(a+b+c).each_with_object({}) do |g,h|
h[g[:name]] = (h[g[:name]] || 0) + g[:value]
end.map { |(name,value)| { name: name, value: value } }
#=> [{:name=>"Identifier", :value=>1500},
# {:name=>"Identifier2", :value=>150}]
Sometimes you might see:
h[k1] = (h[k1] || 0) + g[k2]
written:
(h[k1] ||= 0) + g[k2]
which expands to the same thing.
Another way to calculate h, which I would say is more "Ruby-like", is the following.
h = (a+b+c).each_with_object(Hash.new(0)) do |g,h|
h[g[:name]] += g[:value]
end
This creates the hash represented by the block variable h using the form of Hash::new that takes an argument called the default value:
h = Hash.new(0)
All this means is that if h does not have a key k, h[k] returns the default value, here 0. Note that
h[g[:name]] += g[:value]
expands to:
h[g[:name]] = h[g[:name]] + g[:value]
so if h does not have a key g[:name] this reduces to:
h[g[:name]] = 0 + g[:value]
If you were wondering why h[g[:name]] on the left of the equality was not replaced by 0, it is because that part of the expression employs the method Hash#[]=, whereas the method Hash#[] is used on he right. Hash::new with a default value only concerns Hash#[].
You can do everything in ruby !
Here is a solution to your problem :
d = (a+b+c).group_by { |e| e[:name] }.map { |f| f[1][0].merge(value: f[1].sum { |g| g[:value] }) }
I encourage you to check the Array Ruby doc for more information: https://ruby-doc.org/core-2.7.0/Array.html
I am assuming that the order of identifiers in all arrays is the same. That is {name: 'Identifier', value: ...} always the first element in all 3 arrays, {name: 'Identifier2', value: ... } always the second, etc. In this simple case, a simple each_with_index is a simple and clear solution:
d = []
a.each_with_index do |hash, idx|
d[idx] = {name: hash[:name], value: a[idx][:value] + b[idx][:value] + c[idx][:value] }
end
# Or a more clear version using map:
a.each_with_index do |hash, idx|
d[idx] = {name: hash[:name], value: [a, b, c].map { |h| h[idx][:value] }.sum }
end
A couple different ways, avoiding any finicky array-indexing and the like, (also functionally, since you've added the tag):
grouped = (a + b + c).group_by { _1[:name] }
name_sums = grouped.transform_values { |hashes| hashes.map { _1[:value] }.sum }
name_vals = (a + b + c).map { Hash[*_1.values_at(:name, :value)] }
name_sums = name_vals.reduce { |l, r| l.merge(r) { |k, lval, rval| lval + rval } }
in either case, finish it off with:
name_sums.map { |name, value| { name: name, value: value } }

Why am I getting a Type Error: Range can't be coerced into Integer for line 11, if max == ele % 0?

The following returns a Type Error in my conditional within the do block
Range can't be coerced into Integer
def coprime?(num_1, num_2)
min = [num_1, num_2].min
max = [num_1, num_2].max
[2..min].each do |ele|
if max % ele == 0
return true
end
end
return false
end
p coprime?(25, 12) # => true
p coprime?(7, 11) # => true
p coprime?(30, 9) # => false
p coprime?(6, 24) # => false
Because ranges in Ruby should have the form (start..end). In your case [2..min] is an array with only one element, where that element is a range.
You can replace the [] with () to create a range from 2 to min:
(2..min).each do |ele|
if max % ele == 0
return true
end
end
2..min is a range, therefore [2..min] is actually an array with 1 element and the ele will be 2..min
It should be (2..min).each do |ele| instead of [2..min].each do |ele|
Also you don't need to return false at the end just false is enough.

How to implement CYK parsing algorithm in Ruby?

I am trying to implement CYK algorithm in Ruby according to pseudocode from Wikipedia. My implementation fails to produce the correct parse table. In the method given below, grammar is a member of my own grammar class. Here is the code:
# checks whether a grammar accepts given string
# assumes input grammar to be in CNF
def self.parse(grammar, string)
n = string.length
r = grammar.nonterminals.size
# create n x n x r matrix
tbl = Array.new(n) { |_| Array.new(n) { |_| Array.new(r, false) } }
(0...n).each { |s|
grammar.rules.each { |rule|
# check if rule is unit production: A -> b
next unless rule.rhs.size == 1
unit_terminal = rule.rhs[0]
if unit_terminal.value == string[s]
v = grammar.nonterminals.index(rule.lhs)
tbl[0][s][v] = true
end
}
}
(1...n).each { |l|
(0...n - l + 1).each { |s|
(0..l - 1).each { |p|
# enumerate over A -> B C rules, where A, B and C are
# indices in array of NTs
grammar.rules.each { |rule|
next unless rule.rhs.size == 2
a = grammar.nonterminals.index(rule.lhs)
b = grammar.nonterminals.index(rule.rhs[0])
c = grammar.nonterminals.index(rule.rhs[1])
if tbl[p][s][b] and tbl[l - p][s + p][c]
tbl[l][s][a] = true
end
}
}
}
}
v = grammar.nonterminals.index(grammar.start_sym)
return tbl[n - 1][0][v]
end
I tested it with this simple example:
grammar:
A -> B C
B -> 'x'
C -> 'y'
string: 'xy'
The parse table tbl was the following:
[[[false, true, false], [false, false, true]],
[[false, false, false], [false, false, false]]]
The problem definitely lies in the second part of the algorithm - substrings of length larger than 1. The first layer (tbl[0]) contains correct values.
Help much appreciated.
The problem lies in the translation from the 1-based arrays in the pseudocode to the 0-based arrays in your code.
It becomes obvious when you look at the first indices in the condition tbl[p][s][b] and tbl[l-p][s+p][c] in the very first run of the loop. The pseudocode checks tbl[1] and tbl[1] and your code checks tbl[0] and tbl[1].
I think you have to make the 0-based correction when you access the array and not in the ranges for l and p. Otherwise the calculations with the indices are wrong.
This should work:
(2..n).each do |l|
(0...n - l + 1).each do |s|
(1..l - 1).each do |p|
grammar.rules.each do |rule|
next unless rule.rhs.size == 2
a = grammar.nonterminals.index(rule.lhs)
b = grammar.nonterminals.index(rule.rhs[0])
c = grammar.nonterminals.index(rule.rhs[1])
if tbl[p - 1][s][b] and tbl[l - p - 1][s + p][c]
tbl[l - 1][s][a] = true
end
end
end
end
end

how to get max and sum of 2nd column of array in ruby

for an array like
s = [[1,2],[4,6],[2,7]]
How i can select max and sum of 2nd column in each row in one statement
max= 7
sum= 15
I know, that
sum = 0
max = 0
s.each{ |a,b| sum+=b;if max<b then max = b end }
would work.
The transpose method is nice for accessing "columns":
s = [[1,2],[4,6],[2,7]]
col = s.transpose[1]
p col.max #=> 7
p col.inject(:+) #=> 15
second_elements = s.map { |el| el[1] }
sum = second_elements.inject{|sum,x| sum + x }
max = second_elements.max
To be more clear:
inject{|sum,x| sum + x } returns nil if array is empty, so if you want to get 0 for empty array then use inject(0, :+)
s.max {|a| a[1]}[1] # Max of elements at index 1
s.max {|a| a.last }.last # Max of last elements
# => 7
To find the sum, if you use Ruby 2.4 or greater / if you are on Rails
s.sum {|a| a[1]} # Sum of elements at index 1
s.sum(&:last) # Sum of last elements
# => 15
else
s.inject(0) {|sum, a| sum+= a[1] }
# => 15
s.map{|e| e[1]}.max gives you max
s.map{|e| e[1]}.reduce(:+) gives you sum.
s = [[1,2],[4,6],[2,7]]
second_max = s.max_by(&:last).last
# => 7
sum = s.reduce(0){|sum,a| sum + a.last}
# => 15

Ruby array with an extra state

I'm trying to go through an array and add a second dimension for true and false values in ruby.
For example. I will be pushing on arrays to another array where it would be:
a = [[1,2,3,4],[5]]
I would like to go through each array inside of "a" and be able to mark a state of true or false for each individual value. Similar to a map from java.
Any ideas? Thanks.
You're better off starting with this:
a = [{ 1 => false, 2 => false, 3 => false, 4 => false }, { 5 => false }]
Then you can just flip the booleans as needed. Otherwise you will have to pollute your code with a bunch of tests to see if you have a Fixnum (1, 2, ...) or a Hash ({1 => true}) before you can test the flag's value.
Hashes in Ruby 1.9 are ordered so you wouldn't lose your ordering by switching to hashes.
You can convert your array to this form with one of these:
a = a.map { |x| Hash[x.zip([false] * x.length)] }
# or
a = a.map { |x| x.each_with_object({}) { |i,h| h[i] = false } }
And if using nil to mean "unvisited" makes more sense than starting with false then:
a = a.map { |x| Hash[x.zip([nil] * x.length)] }
# or
a = a.map { |x| x.each_with_object({}) { |i,h| h[i] = nil } }
Some useful references:
Hash[]
each_with_object
zip
Array *
If what you are trying to do is simply tag specific elements in the member arrays with boolean values, it is just a simple matter of doing the following:
current_value = a[i][j]
a[i][j] = [current_value, true_or_false]
For example if you have
a = [[1,2,3,4],[5]]
Then if you say
a[0][2] = [a[0,2],true]
then a becomes
a = [[1,2,[3,true],4],[5]]
You can roll this into a method
def tag_array_element(a, i, j, boolean_value)
a[i][j] = [a[i][j], boolean_value]
end
You might want to enhance this a little so you don't tag a specific element twice. :) To do so, just check if a[i][j] is already an array.
Change x % 2 == 0 for the actual operation you want for the mapping:
>> xss = [[1,2,3,4],[5]]
>> xss.map { |xs| xs.map { |x| {x => x % 2} } }
#=> [[{1=>false}, {2=>true}, {3=>false}, {4=>true}], [{5=>false}]]

Resources