Count the occurence of values in Ruby - ruby

I'm trying to count numbers of different values in mysql table columns. Range of possible values is integer and from 0-10. The following code is working, but I'm wondering if there is a more elegant way to do this?
# Example data from Mysql
result = [{ :column1 => "2", :column2 => "3", :column3 => "1"},{ :column1 => "2", :column2 => "3", :column3 => "1"},{ :column1 => "1", :column2 => "2", :column3 => "3"}]
# Init hash
final_result = Hash.new { |h, k| h[k] = { } }
# Loop result columns
result.each do |single_column|
# Loop single items inside columns
single_column.each do |single_result|
# Create column if does not exist
if final_result[single_result[0]][single_result[1]].nil? then
final_result[single_result[0]][single_result[1]] = 1
else
final_result[single_result[0]][single_result[1]] += 1
end
end
end
puts final_result
# => {:column1=>{"2"=>2, "1"=>1}, :column2=>{"3"=>2, "2"=>1}, :column3=>{"1"=>2, "3"=>1}}

There's some room for cleaning up here. The most obvious part is that long, clunky if statement. The test vs nil? is pointless, remember in Ruby the only things that are logically false are false and nil, so since false is never going to show up here, the test vs. nil specifically can be removed.
More than that, though, you're on the right track with the custom Hash.new call, but you don't go far enough. Why not initialize the second tier with zero?
That results in code that looks like:
result = [
{ :column1 => "2", :column2 => "3", :column3 => "1"},
{ :column1 => "2", :column2 => "3", :column3 => "1"},
{ :column1 => "1", :column2 => "2", :column3 => "3"}
]
# Init hash
final_result = Hash.new { |h, k| h[k] = Hash.new(0) }
# Loop result columns
result.each do |single_column|
single_column.each do |r|
final_result[r[0]][r[1]] += 1
end
end
puts final_result.inspect

Have a look at the active record count method (doc link). You can use that in combination with group to do what you are trying to achieve.
[:column1, :column2, :column3].inject({}) do |hash, column|
hash[column] = Model.group(column).count
hash
end

Related

Merging two arrays of hashes in Ruby

I’m very close to solving a project I’ve been working on for a while but can’t seem to figure this out.
array1 = [{:new_listing => "1", :item => "apple"}, {:new_listing => "2", :item => "bannana"}]
array2 = [{:height => "10"}, {:height => "12"}]
How do I merge them so it is
[{:new_listing => "1", :item => "apple", :height => "10" },
{:new_listing => "2", :item => "bannana", :height => "12"}]
The order of each arrays are aligned, and should be the same size. Some values of array2 will be {:height => nil}.
The order of each arrays are aligned, and should be the same size.
That's exactly the description of the zip method.
zip(arg, ...) → an_array_of_array
Takes one element from enum and merges corresponding elements from each args.
Likewise, merging hashes is done with merge
array1.zip(array2).map { |x, y| x.merge(y) }
array1.map {|x| x.merge!(array2.shift) }
use map/each as per your requirement
Below solution should work for you:
array1 = [{:new_listing=> "1", :item=> "apple"}, {:new_listing=> "2", :item=> "bannana"}]
array2 = [{:height=> "10"},{:height => "12"}]
array2.each_with_index { |hash, index| array1[index] = array1[index].merge(hash) }
puts array1
# [{:new_listing=>"1", :item=>"apple", :height=>"10"}, {:new_listing=>"2", :item=>"bannana", :height=>"12"}]

how can i perform a query on a hash, and get result another hash?

I am trying to do a query against a ruby hash, which is much alike this:
{"client1" => {"tag" => "13", "host" => "client1.example.com", ...}, "client2" => {"tag" => "11", ...} }
and I would like to map it to only the client names with their tags, like this:
{"client1" => "13", "client2" => "11"}
I have been struggeling with .each and .select and .find but haven't figured it out yet. I am pretty sure it is not that hard, does anybody know? Thanks
You could do the same as below
data = {
"client1" => {"tag" => "13", "host" => "client1.example.com"},
"client2" => {"tag" => "11"}
}
desired_data = Hash.new
data.each do |k,v|
desired_data[k] = v["tag"]
end
desired_data will contain your result.
As suggested by #sawa you could also use
data.each_with_object({}){|(k, v), h| h[k] = v["tag"]}
Use map:
test_hash = {"client1" => {"tag" => "13", "host" => "client1.example.com"}, "client2" => {"tag" => "11"} }
test_hash.map{|k,v| [k, v['tag']]}.to_h
#=> {"client1"=>"13", "client2"=>"11"}
One way is to merge the hash with itself, using the form of Hash#merge that employs a block to determine the values of keys that are present in both hashes being merged, which in this case is all keys.
h = {"client1" => {"tag" => "13", "host" => "client1.example.com"},
"client2" => {"tag" => "11"} }
h.merge(h) { |*,v| v["tag"] }
#=> {"client1"=>"13", "client2"=>"11"}
As explained in the doc, the block has three variables, often written |key, old_value, new_value|. Here old_value and new_value are the same. The asterisk in |*, new_value| is a placeholder for all but the last block variable.

Break an Each statement once an inside IF statement is run once in Ruby

In the following code how do I break the each statement once the if code successfully changes the very first 1 it comes across to 2.
hash = {:key1 => "1", :key2 => "2", :key3 => "1", :key4 => "3" :key5 => "3"}
array = [:key1,:key2,:key3,:key4,:key5]
array.each do |x|
if hash[x] == "1"
hash[x] = 2
end
Something like below you want
array = [1,2,2,1,2]
array.each do |x|
break x = 2 if x == 1
end

Ruby - Array of Hashes, Trying to Select Multiple Keys and Group By Key Value

I have a set of data that is an array of hashes, with each hash representing one record of data:
data = [
{
:id => "12345",
:bucket_1_rank => "2",
:bucket_1_count => "12",
:bucket_2_rank => "7",
:bucket_2_count => "25"
},
{
:id => "45678",
:bucket_1_rank => "2",
:bucket_1_count => "15",
:bucket_2_rank => "9",
:bucket_2_count => "68"
},
{
:id => "78901",
:bucket_1_rank => "5",
:bucket_1_count => "36"
}
]
The ranks values are always between 1 and 10.
What I am trying to do is select each of the possible values for the rank fields (the :bucket_1_rank and :bucket_2_rank fields) as keys in my final resultset, and the values for each key will be an array of all the values in its associated :bucket_count field. So, for the data above, the final resulting structure I have in mind is something like:
bucket 1:
{"2" => ["12", "15"], "5" => ["36"]}
bucket 2:
{"7" => ["25"], "9" => ["68"]}
I can do this working under the assumption that the field names stay the same, or through hard coding the field/key names, or just using group_by for the fields I need, but my problem is that I work with a different data set each month where the rank fields are named slightly differently depending on the project specs, and I want to identify the names for the count and rank fields dynamically as opposed to hard coding the field names.
I wrote two quick helpers get_ranks and get_buckets that use regex to return an array of fieldnames that are either ranks or count fields, since these fields will always have the literal string "_rank" or "_count" in their names:
ranks = get_ranks
counts = get_counts
results = Hash.new{|h,k| h[k] = []}
data.each do |i|
ranks.each do |r|
unless i[r].nil?
counts.each do |c|
results[i[r]] << i[c]
end
end
end
end
p results
This seems to be close, but feels awkward, and it seems to me there has to be a better way to iterate through this data set. Since I haven't worked on this project using Ruby I'd use this as an opportunity to improve my understanding iterating through arrays of hashes, populating a hash with arrays as values, etc. Any resources/suggestions would be much appreciated.
You could shorten it to:
result = Hash.new{|h,k| h[k] = Hash.new{|h2,k2| h2[k2] = []}}
data.each do |hsh|
hsh.each do |key, value|
result[$1][value] << hsh["#{$1}_count".to_sym] if key =~ /(.*)_rank$/
end
end
puts result
#=> {"bucket_1"=>{"2"=>["12", "15"], "5"=>["36"]}, "bucket_2"=>{"7"=>["25"], "9"=>["68"]}}
Though this is assuming that :bucket_2_item_count is actually supposed to be :bucket_2_count.

How to detect value in array of hashes

I have array of hashes:
#array = [{:id => "1", :status=>"R"},
{:id => "1", :status=>"R"},
{:id => "1", :status=>"B"},
{:id => "1", :status=>"R"}]
How to detect, does it contain in hashes with the value of status "B"? Like in simply array:
#array = ["R","R","B","R"]
puts "Contain B" if #array.include?("B")
Use any?:
#array.any? { |h| h[:status] == "B" }
Arrays (enumerables actually) have a detect method. It returns a nil if it doesn't detect anything, so you can use it like Andrew Marshall's any.
#array = [{:id => "1", :status=>"R"},
{:id => "1", :status=>"R"},
{:id => "1", :status=>"B"},
{:id => "1", :status=>"R"}]
puts "Has status B" if #array.detect{|h| h[:status] == 'B'}
Just to add to what steenslag said:
detect doesn't always return nil.
You can pass in a lambda to execute (call) if detect does not 'detect' (find) an item. In other words, you can tell detect what to do if it can't detect (find) something.
To add to your example:
not_found = lambda { "uh oh. couldn't detect anything!"}
# try to find something that isn't in the Enumerable object:
#array.detect(not_found) {|h| h[:status] == 'X'}
will return "uh oh. couldn't detect anything!"
This means that you don't have to write this kind of code:
if (result = #array.detect {|h| h[:status] == 'X'}).nil?
# show some error, do something here to handle it
# (this would be the behavior you'd put into your lambda)
else
# deal nicely with the result
end
That's one major difference between any? and detect -- you can't tell any? what to do if it doesn't find any items.
This is in the Enumerable class. ref: http://ruby-doc.org/core/classes/Enumerable.html#M003123

Resources