Print keys for certain value - ruby

I have a hash that looks similar to:
hash = {key1: true, key2: false, key3: false, key4: true}
and I would like to iterate through the hash and print each key which has a true value. The result should look like:
key1
key4
How am I going to do that? I tried:
hash.each do |k,v|
puts k if true
end

While iterating is fine, the goal might be achieved in more rubyish manner:
hash.select { |_, v| v }.keys
or, if equality to true (as an opposite to being just truthy) is significant:
hash.select { |_, v| v == true }.keys
To print the result out:
puts hash.select { |_, v| v == true }.keys
Further information on how Hash#select works.
To print all the keys matched as “key1 and key4”:
puts hash.select { |_, v| v == true }.keys.join(' and ')

hash.each do |k, v|
puts k if v == true
end

You can use map and compact methods:
hash.map { |k, v| k if v }.compact

It is as simple as:
hash.each do |k,v|
puts k if v
end

Related

Counting number of string occurrences in a string/array

I'm expecting to return all words with the max occurrences in a given string. The following code is expected to do so:
t1 = "This is a really really really cool experiment cool really "
frequency = Hash.new(0)
words = t1.split
words.each { |word| frequency[word.downcase] += 1 }
frequency = frequency.map.max_by { |k, v| v }
puts "The words with the most frequencies is '#{frequency[0]}' with
a frequency of #{frequency[1]}."
The output is:
The words with the most frequencies is 'really' with
a frequency of 4.
However, it does not work if there are, for example two strings that equal to the max. For example, if I add three cools to the text, it would still return the same output even though the count of cool is also equal to four.
It would be nice if you could tell me if those method would work on an array too instead of a string.
Try this.
t1 = "This is a really really really cool cool cool"
Step 1: Break your string into an array of words
words = t1.split
#=> ["This", "is", "a", "really", "really", "really", "cool", "cool", "cool"]
Step 2: Compute your frequency hash
frequency = Hash.new(0)
words.each { |word| frequency[word.downcase] += 1 }
frequency
##=> {"this"=>1, "is"=>1, "a"=>1, "really"=>3, "cool"=>3}
Step 3: Determine the maximum frequency
arr = frequency.max_by { |k, v| v }
#=> ["really", 3]
max_frequency = arr.last
#=> 3
Step 4: Create an array containing words having a frequency of max_frequency
arr = frequency.select { |k, v| v == max_frequency }
#=> {"really"=>3, "cool"=>3}
arr.map { |k, v| k }
#=> ["really", "cool"]
Conventional way of writing this in Ruby
words = t1.split
#=> ["This", "is", "a", "really", "really", "really", "cool", "cool", "cool"]
frequency = words.each_with_object(Hash.new(0)) do |word, f|
f[word.downcase] += 1
end
#=> {"this"=>1, "is"=>1, "a"=>1, "really"=>3, "cool"=>3}
max_frequency = frequency.max_by(&:last).last
#=> 3
frequency.select { |k, v| v == max_frequency }.map(&:first)
#=> ["really", "cool"]
Notes
e = [1,2,3].map #=> #<Enumerator: [1, 2, 3]:map>. This tells us that frequency.map.max_by { |k,v| v } is the same as frequency.max_by { |k,v| v }.
In frequency = frequency.map.max_by {|k, v| v }, frequency on the right is a hash; frequency on the left is an array. It's generally consider bad practice to reuse variables in that way.
Often frequency.max_by { |k,v| v } is written frequency.max_by { |_,v| v } or frequency.max_by { |_k,v| v }, mainly to signal to the reader that the first block variable is not used in the block calculation. (As I indicated above, this statement would generally be written frequency.max_by(&:last).) Note _ is a valid local variable.
frequency.max_by { |k, v| v }.last could instead be written frequency.map { |k, v| v }.max but that has the disadvantage that map produces an intermediate array of frequence.size elements, whereas the former produces an intermediate array of two elements.
You've already found the most frequent
greatest_frequency = frequency.max_by {|_, v| v }
Let's use it to found all the words which have this frequency
most_frequent_words = frequency.select { |_, v| v == greatest_frequency }.keys
puts "The words with the most frequencies are #{most_frequent_words.join(', ')} with a frequency of #{greatest_frequency}."
string = 'This is is a really a really a really cool cool experiment a cool cool really'
1). Separate string into array of words
words = string.split.map(&:downcase)
2). Calculate maximum frequency based on unique words
max_frequency = words.uniq.map { |i| words.count(i) }.max
3). Find combinations of word and frequency
combos = words.group_by { |e| e }.map { |k, v| [k, v.size] }.to_h
4). Select most frequent words
most_frequent_words = combos.select { |_, v| v == max_frequency }.keys
Result
puts "The words with the most frequencies are '#{most_frequent_words.join(', ')}' with a frequency of #{max_frequency}."
#=> The words with the most frequencies are 'a, really, cool' with a frequency of 4.

Best way to convert hash keys to array only if their values are set to true

I'm trying to find an elegant and compact way to convert hash keys into array that contains only those that have true as value
example = {"foo" => true, "bar" => false, "baz" => true}
become
example = ["foo", "baz"]
example = example.keys.select {|key| example[key].eql? true}
p example
output
["foo", "baz"]
The shortest would be example.select{|k, v| v}
to extract the keys simply add .keys
EDIT: if like Cary suggests there would be other than boolean values you would have to check for v == true or v.eql? true
My 2 cents:
example.collect{|k, v| k if v}.compact
output: ["foo", "baz"]
Which can work also picking false:
example.collect{|k, v| k if !v}.compact
output: ["bar"]
..or
There are a lot of different ways to do this. Here's another one:
example.reduce([]) { |memo, (k, v)| v ? memo << k : memo }
Or, similarly:
example.each_with_object([]) { |(k, v), memo| memo << k if v }
Or you can use my nutty piecewise gem:
example.piecewise { |yielder, (k, v)| yielder << k if v }

How to split a hash in two hashes based on a condition?

I have a hash:
input = {"a"=>"440", "b"=>"-195", "c"=>"-163", "d"=>"100"}
From it I want to get two hashes, one containing the pairs whose value (as integer) is positive, the other containing negative values, for example:
positive = {"a"=>"440", "d"=>"100" }
negative = {"b"=>"-195", "c"=>"-163" }
How can I achieve this using the minimum amount of code?
You can use the Enumerable#partition method to split an enumerable object (like a hash) based on a condition. For example, to separate positive/negative values:
input.partition { |_, v| v.to_i < 0 }
# => [[["b", "-195"], ["c", "-163"]],
# [["a", "440"], ["d", "100"]]]
Then, to get the desired result, you can use map and to_h to convert the key/value arrays to hashes:
negative, positive = input.partition { |_, v| v.to_i < 0 }.map(&:to_h)
positive
# => {"a"=>"440", "d"=>"100"}
negative
# => {"b"=>"-195", "c"=>"-163"}
If you use a version of Ruby prior 2.1 you can replace the Array#to_h method (that was introduced in Ruby 2.1) like this:
evens, odds = input.partition { |_, v| v.to_i.even? }
.map { |alist| Hash[alist] }
This implementation uses Enumerable#group_by:
input = {"a"=>"440", "b"=>"-195", "c"=>"-163", "d"=>"100"}
grouped = input.group_by { |_, v| v.to_i >= 0 }.map { |k, v| [k, v.to_h] }.to_h
positives, negatives = grouped.values
positives #=> {"a"=>"440", "d"=>"100"}
negatives #=> {"b"=>"-195", "c"=>"-163"}
I must say that Enumerable#partition is more appropriate, as #toro2k answered.
something like this then?
positive = Hash.new
negative = Hash.new
input.each_pair { |var,val|
if val.to_i > 0
positive[var] = val
else
negative[var] = val
end
}

Ruby hash keys to array conditional on hash value

I would like to extract hash key values to an array when a condition is met. For example, with hash h I want to extract the keys where the values are "true":
h = { :a => true, :b => false, :c =>true }
I've come up with this:
h.map {|k,v| k if v==true} - [nil]
Any alternatives?
h.select { |_, v| v }.keys
Will do the same, but in more readable way.
You can also do
s = {}
h.each do |k,v|
s[k] = v if v==true
end

Ruby 1.8: Hash#sort not return hash but array (better way to do this?)

In some scenario of Ruby 1.8. If I have a hash
# k is name, v is order
foo = { "Jim" => 1, "bar" => 1, "joe" => 2}
sorted_by_values = foo.sort {|a, b| a[1] <==> b[1]}
#sorted_by_values is an array of array, it's no longer a hash!
sorted_by_values.keys.join ','
my workaround is to make method to_hash for Array class.
class Array
def to_hash(&block)
Hash[*self.collect { |k, v|
[k, v]
}.flatten]
end
end
I can then do the following:
sorted_by_values.to_hash.keys.join ','
Is there a better way to do this?
Hashes are unordered by definition. There can be no such thing as a sorted Hash. Your best bet is probably to extract the keys from the sorted array using collect and then do a join on the result
sortedByValues = foo.sort {|a, b| a[1] <==> b[1]}
sortedByValues.collect { |a| a[0] }.join ','

Resources