syntax for .max/.min and sum of hash values - ruby

Trying to get a value out of 2d array inside of a hash and determine its max or min value
This is what I have so far
pretend_hash = { 333 => [Dog,19.99], 222=> [Cat,25.55] }
if a == 5 # Loop for view highest priced product"
puts "View highest priced product"
puts pretend_hash.values.max
end
So this returns the highest alphabetical value in the first part of the array by default
Which would be Dog. How do I go about getting access to the 2nd part of the array where 25.55 gets declared the .max value? Something like pretend_hash.values.max[|k,v,z| print z]
or a reverse on something?
The other problem I'm having is iterating through 2nd hash elements and determining the sum. Again callling that 2nd element in the array is something I'm not able to find the syntax for. I mean its not hard to say 19.99+25.55 = some variable and slam it into a puts. I'm assuming its something like:
pretend_hash.sum{|k,v,z| ?? }
#I assume it iterates through the z element
#and adds all current z values and returns the sum?

Min/max can be solved like this:
pretend_hash.values.sort{|x,y| x[1] <=> y[1]}[0] # Gives min, -1 will be max
Or:
pretend_hash.values.map{|x| x[1]}.max
And sum can be had like this:
pretend_hash.values.inject(0){|sum,x| sum+x[1]}

pretend_hash = { 333 => ["Dog",19.99], 222=> ["Cat",25.55] }
key,value = pretend_hash.max{|a,b| b[1] <=> a[1]}
puts key.to_s
puts value.join(',')
puts pretend_hash.inject(0){|sum, hash| sum + hash[1][1]}.to_s
#returns:
#222
#Cat,25.55
#45.54

Related

Ruby: If Values in a hash are equal if statement. Need a hint if possilbe?

Hi there: Ruby Beginner.
So I'm working on a little exercise.
I have this
donation = {
"Randy" => 50,
"Steve" => 50,
"Eddie" => 9,
"Bill" => 12
}
donation.max_by.first { |name, money| money.to_i }
name, money = donation.max_by { |name, money| money.to_i }
puts "#{name} donated the most, at $#{money}!"
But there's a little bug. "Randy" and "Steve" both donated the max, but it outputs "Randy" (because they're the first in key in the for hash, I assume?) and they get all the credit!
I'm thinking the way to go is, I need an IF ELSE statement; IF any? of the "money" values are equal, it moves on. Else, it puts the statement.
SO i guess I am wondering how to compare values?
Select Elements Matching Highest Value
There's more than one way to identify and extract multiple elements that share a maximum value. Based on the semantics you're trying to communicate in your code, one working example is as follows:
max_amt = donation.max_by(&:last)[-1]
donation.select { |name, amt| amt == max_amt }
#=> {"Randy"=>50, "Steve"=>50}
First, we capture the maximum value from the donations Hash. Next, the Hash#select method passes the name and amount of each donation into the block, which returns the Hash elements for which the comparison to the maximum value is truthy. Inside the block, each amount is compared to the maximum value found in the original Hash, allowing all matching key/value pairs to be returned when more than one of them contains a value equal to the max_amt.
As you discovered, max_by returns the first key that has the maximum value. It actually takes a parameter that is the number of elements to return, so doing something like .max_by(donation.size) will return the hash in descending order and you could then go through the elements until the value doesn't match the first (max) value.
But, there's a couple of simpler ways to go about it. If you just want to print the information as you go, one way would be to just iterate through the hash looking for values that match the max value:
mx = donation.values.max
puts "The following people donated the most ($#{mx}):"
donation.each { |k, v| puts k if v == mx }
Output:
The following people donated the most ($50):
Randy
Steve
You could also use .select to match for the max value, which preserves the hash form:
mx = donation.values.max
donation.select { |k, v| v == mx }
=> {"Randy"=>50, "Steve"=>50}
EDIT
Per the follow-up comment, if you use .select to capture the result as a new hash, you can then use conditional logic, loops, etc., to process the data from there. As a very simple example, suppose you want a different message if there's only one top donor vs. multiple:
mx = donation.values.max
max_donors = donation.select { |k, v| v == mx }
if max_donors.size > 1
puts "The following people donated the most ($#{mx}):"
max_donors.keys.each { |name| puts name }
elsif max_donors.size == 1
name, money = max_donors.first
puts "#{name} donated the most, at $#{money}!"
else
puts 'No one donated!'
end

Using Ruby filter in logstash to sum values of a field everytime that event appears

I'm trying to sum the value of a specific field every time it shows, the field is in this format: [cdr][Service-Information][PS-Information][Service-Data-Container][Accounting-Output-Octets] and its value is a numeric field (it shows the number of bits consumed).
What I'm trying to do is the following:
a = event.get("[cdr][Service-Information][PS-Information][Service-Data-Container][Accounting-Output-Octets]")
if a
sum = 0
a.each_index { |x|
sum += a["amount"]
}
event.set("amount-sum", sum)
end
I'm getting the following error:
Ruby exception occurred: undefined method `each_index' for Integer
I am a newbie in Ruby, so I've got no idea if this code serves for this type of field too.
If a is integer You can use something like this:
a = 123456789
a = a.to_s.split("").map(&:to_i)
sum = 0
a.each do |x|
sum += x
end
p sum #=> 45
And please DO NOT USE brace if block is not in one line. Here I did it with do end just to show how it must be. If You want to use one line block - You must write like that:
a.each {|x| sum += x}
if block > 1 lines than use do/end | else { }

Select array elements that are greater than 5% of a sum

sum = #products.inject(0){ |sum,item| sum += item['count'] }
#selected = #products.select { |item| (item['count']/sum) >= 0.05 }
I want to select every element from the #products array whose count property is greater than 5% of the sum. #products is an array of hashes.
However, when I use this second line, #selected returns an empty array. After finding no fault with |item| or the #products array itself, I'm inclined to believe it has something to do with trying to use an external variable, sum, inside the .select block. Could someone explain to me why #selected is returning nothing?
Write as below :
#selected = #products.select { |item| (item['count'].to_f/sum) >= 0.05 }
You need to make either item['count'] or sum as floating point number to get floating point number,after the division. Quick example to prove my words in PRY
(arup~>~)$ pry --simple-prompt
>> 12/13
=> 0
>> 12/13.to_f
=> 0.9230769230769231
>> 12.to_f/13
=> 0.9230769230769231
>>
If the counts are integers, item['count']/sum will always be zero due to integer division.
Try the following instead:
#selected = #products.select { |item| item['count'] >= 0.05 * sum }

Max value of nested array in hash

What I have:
hash = {id =>[string, number], id =>[string, number]}
I need to get the max value of number. I have been able to do this, but when I puts it.
I get:
id
string
number
What I need please
id string number
This is what I've tried:
This brings the max value to the top of list, but I need to exclude the rest of the list.
hash.each{|x, y| puts "#{x} #{y[0]} #{y[1]}"}.max
This returns the max value but displays it vertically
puts hash.max_by{|y| "#{y}"}
I have tried numerous other things and am having a hard time wrapping my head around this one.
Not sure if it matters but I am read this in from a file into a hash, the number is a float
The max here doesn’t do anything (since it is called on hash and its return value never used):
hash.each{|x, y| puts "#{x} #{y[0]} #{y[1]}"}.max
This is the same as doing puts on an array (since that’s what max_by returns), which prints each element on a separate line. You’re also unnecessarily converting your number to a string (which can result in unexpected comparison results):
puts hash.max_by{|y| "#{y}"}
Instead let’s just get the max key/value pair:
max = hash.max_by { |id, (string, number)| number }
#=> ["the-id", ["the-string", 3.14]]
Now we can flatten and join the array before puts-ing it:
puts max.flatten.join(' ')
# prints: the-id the-string 3.14
I would re-arrange the hash with number as the key, then use sort_by(): http://www.rubyinside.com/how-to/ruby-sort-hash

Storing output into a variable to be used in an array

A snippet of my code below flips a coin and outputs a result of 10 total heads or tails.
(e.g. Heads Tails Heads Tails...)
I'd like to store this into a variable where I can put it into an array and use its strings.
%w[act] only outputs the string "act". How can I get that line of code to output my array of strings from the line act = coin.flip?
Updated and added full code
class Coin
def flip
flip = 1 + rand(2)
if flip == 2
then puts "Heads"
else
puts "Tails"
end
end
end
array = []
10.times do
coin = Coin.new
array << coin.flip
end
puts array
This:
10.times do
coin = Coin.new
act = coin.flip
end
doesn't produce an array. It simply creates ten coin flips and throws them all away, the result of that expression is, in fact, 10. If you want an array, you'll need to build one.
You could take Douglas's approach or try something a bit more idiomatic.
The Integer#times method returns an enumerator so you can use any of the Enumerable methods on it rather than directly handing it a block. In particular, you could use collect to build an array in one nice short piece of code:
a = 10.times.collect { Coin.new.flip }
That gives you 10 flips in the Array a and then you can puts a or puts a.join(', ') or whatever you want.
The %w[] won't work because that's for generating an Array of whitespace separated words:
%w[] Non-interpolated Array of words, separated by whitespace
So %w[a b c] is just a nicer way of saying ['a', 'b', 'c'] and the words within %w[] are treated as single quoted strings rather than variables or method calls to be evaluated.
Seems that there is some editing going on. You'll also want to modify your flip method to return the flip rather than print it:
def flip
flip = 1 + rand(2)
if flip == 2
"Heads"
else
"Tails"
end
end
Then you'll get your Heads and Rails in the array.
Put the act results into an array.
arr = []
10.times do
coin = Coin.new
arr << coin.flip
end
p arr # => [...]

Resources