Count of a count with Rails - ruby-on-rails

Using Usage.group(:song_id), I can get all the usages of any particular song in my app. Using Usage.group(:song_id).count, I can get a hash like {song_id => usage_count ...}.
How do I produce a count of that count though? i.e something like this:
[
used_once: number_of_songs_used_once,
used_twice: number_of_songs_used_twice,
used_thrice: number_of_songs_used_thrice,
etc.
]
(Okay, so really I would expect output to look something like {1=>14, 2=>6, 3=>2, 4=>1}).

You can use inject on the hash.
song_values = Usage.group(:song_id).count.values
times_used = song_values.inject({}) do |used, count|
if used[count].nil?
used[count] = 1
else
used[count] += 1
end
used
end
You could use a ternary operator if you want the if/else to be one line
used[count].nil? ? used[count] = 1 : used[count] += 1
This is just looping over the songs usage count building a hash where the key is the number of times that song was used, updating the value for each count (not explained that particularly well but I hope you understand).

Related

Most Efficient Way to Sort By String, Based on Defined Array in Ruby on Rails

I am trying to find the most efficient way to sort an array based on a defined order from another array and display it in my presenter. There is a property of the array that needs sorting, that I want to sort on, 'name'. So there is a function, 'dogs_available' that returns a collection of objects that looks like:
[{name:'Timmy', size:'small', eyes:'brown'},{name:'Rico', size:'large', eyes:'purple'}]
So I want
sort_order = ['Timmy', 'Charlie', 'Rico', 'Hannah']
m.dogs_available.sort do |x,y|
x = sort_order.index x.name
y = sort_order.index y.name
if x.nil?
-1
elsif y.nil?
-1
else
x <=> y
end
end
And then to print it out (just for testing, I'll clean it up later) it looks like:
dogs = m.dogs_available
text = ""
dogs.each do |dog|
text += "<h4>#{dog.name}</h4>"
end
text.html_safe
For starters, this sort doesn't work. Also, I am confident this isn't the best way to sort/compare. Any pointers?
Your existing sort logic can be replaced with
m.dogs_available.sort_by{|hash| sort_order.index(hash[:name]) || -1}
however, where is dogs_available coming from? Usually in rails you'd be pulling data out of the database, and you can do your sorting then.

Ruby random selection from array sometimes returns same value repeatedly

I am using this to select a random Matchup, and I test it's results, and draw a random Matchup until it meets the criteria of a while loop:
m = Matchup.order("RANDOM()").first
The loop is set to break after 10 cycles (to avoid infinite loops), and I will arbitrarily break out of the loop, check the logs, and see that the Matchup is the same every time it went through the loop. A simplified version of the loop would be something like this:
counter = 0
while counter < 5
m = Matchup.order("RANDOM()").first
logger.debug('Matchup ID: ' + m.id)
counter += 1
end
The log will look like this:
Matchup ID: 7
Matchup ID: 7
Matchup ID: 7
Matchup ID: 7
Matchup ID: 7
Why would m = Matchup.order("RANDOM()").first arbitrarily not pull a different Matchup? The strangest part is that sometimes it works without issue, and others it gets stuck in the loop b/c m is not changing. Any suggestions?
If you want to see the actual loop, you can see it here (in the getRandomMatchup function):
https://github.com/jackerman09/wdis/blob/master/app/controllers/static_pages_controller.rb
To add another variant :)
ids = Matchup.pluck(:id)
m = Matchup.find( ids.shuffle.first )
This will work well unless the set of id values is excessive, in which case you are shuffling a very large array. However, in the 1k to 2k set size, it will not be noticeable to a user.
Or simply:
ids = Matchup.pluck(:id)
m = Matchup.find( ids.sample )
Will choose a random item from the ids array.
You can use shuffle for this:-
Matchup.all.shuffle!.first
Assuming this model is a normal ActiveRecord model, You could try something like:
max_id = Matchup.maximum(:id)
id = rand(max_id)
m = Matchup.find(id)
...
Doing so might be cheaper than asking the database to get you randomly ordered records. However, the downside of this approach is that records may be deleted, so Matchup.find(id) may be nil.
Another, better, way to do this might be:
ids = Matchup.pluck(:id)
m = Matchup.find(ids[rand(ids.length)])

checking if a set of objects is present in an array a number of times RoR

I'm trying to check if a set of objects which I turn with a .each method are present in an array a given number of times? DOes anyone know if it is possible?
I hope I've been clear
if I understand what you're asking, this might be what you want:
list = ["a","b","b","c","c","c"]
array = ["a","b","c"]
Suppose you have these arrays above, running the following would give you a hash "number" with the number of times that an element inside "array" is present inside "list"
number = {}
array.each do |key|
number[key] = list.select{|item| item == key}.size
end
This way, number[array.first] gives you 1. That's because "a" (array.first) is present 1 time inside list. number["c"] would give you 3.
Assuming I understand the question:
Given two arrays:
a1 = [1,1,2,3,3,4,5]
a2 = [1,3,5]
You can check how many times each element of the second array appears in the first in a few different ways. Here's one:
items_present = true
a2.each do |thing|
if a1.select{|x| x == thing }.size < 2
items_present = false
break
end
end
items_present will be true if each thing appears at least two times.

How do I count items in an array that have a specific attribute value?

In my application, I have an array named #apps which is loaded by ActiveRecord with a record containing the app's name, environment, etc.
I am currently using #apps.count to get the number of apps in the array, but I am having trouble counting the number of applications in the array where the environment = 0.
I tried #apps.count(0) but that didn't work since there are multiple fields for each record.
I also tried something like #apps.count{ |environment| environment = 0} but nothing happened.
Any suggestions?
Just use select to narrow down to what you want:
#apps.select {|a| a.environment == 0}.count
However, if this is based on ActiveRecord, you'd be better off just making your initial query limit it unless of course you need all of the records and are just filtering them in different ways for different purposes.
I'll assume your model is call App since you are putting them in #apps:
App.where(environment: 0).count
You have the variable wrong. Also, you have assignment instead of comparison.
#apps.count{|app| app.environment == 0}
or
#apps.count{|app| app.environment.zero?}
I would use reduce OR each_with_object here:
reduce docs:
#apps.reduce(Hash.new(0)) do |counts, app|
counts[app.environment] += 1
counts
end
each_with_object docs:
#apps.each_with_object(Hash.new(0)) do |app, counts|
counts[app.environment] += 1
end
If you are able to query, use sql
App.group(:environment).count will return a hash with keys as environment and values as the count.

Splitting an Array in Ruby

I am fetching some data from the database as below in my ruby file:
#main1= $connection.execute("SELECT * FROM builds
WHERE platform_type LIKE 'TOTAL';")
#main2= $connection.execute("SELECT * FROM builds
WHERE platform_type NOT LIKE 'TOTAL';")
After doing this I am performing hashing and a bunch of other stuff on these results. To be clear, this does not return an array as such, but it returns some mysql2 type object. So I just map it to 2 arrays to be safe:
#arr1 = Array.new
#arr1 = #main1.map
#arr2 = Array.new
#arr2 = #main2.map
Is there any way to avoid executing 2 different queries and getting all the results in 2 different arrays by executing just one query. I basically want to split the results into 2 arrays, the first one having platform_type = TOTAL and everything else in the other one.
Also without getting into why you're doing what you're doing, I would use Enumerable#partition as such:
rows = $connection.execute('SELECT * FROM builds')
like_total, not_like_total = rows.partition { |row|
row['platform_type'] =~ /TOTAL/
}
Note that, IIRC, SQL LIKE 'TOTAL' isn't the same as Ruby's "string" =~ /TOTAL/ (which is more like LIKE '%TOTAL%' in SQL—am not sure what you need).
To answer your question without getting into why you're doing it like that:
Return them all in one query, with the extra criteria, then you can group them however you want with group_by:
all_results = $connection.execute("SELECT *, platform_type LIKE 'TOTAL' as is_like_total FROM builds").
This will give each of your results an 'is_like_total' "column" that you can group_by on.
http://ruby-doc.org/core-2.0/Enumerable.html#method-i-group_by

Resources