2
\$\begingroup\$

I'm trying to figure out if there's a better way to run the code below. Basically Course is an association of Student and courses_needed is an array of Course where I have many Students have and need many Courses and I'm trying to figure out the demand. I've came up with the following code and it runs, which is fine, but when I want to display it on a statistics page with 36 Courses, the same code needs to be run once for each Course.

I've solved it now by having it run once a day, instead of real-time.

 def self.calculate_needed
 Course.all.each do |course|
 neededArray = Array.new
 needed = 0
 Student.all.each do |s|
 unless s.courses_needed.nil?
 s.courses_needed.each do |c|
 if c == course
 needed += 1
 end
 end
 end
 end
 course.needed = needed
 course.save
 end
 end
Nakilon
1,3387 silver badges17 bronze badges
asked Mar 25, 2016 at 1:56
\$\endgroup\$
2
  • \$\begingroup\$ Your database can produce this info in realtime with a single query, without using any Ruby. Please add the relationship (e.g., 'has_many') of Course and Student. \$\endgroup\$ Commented Mar 25, 2016 at 11:24
  • \$\begingroup\$ This relationship is already added. What I want is an integer, so I can enter it in Morris Charts. How would I produce this query in Ruby on Rails? \$\endgroup\$ Commented Mar 25, 2016 at 12:21

1 Answer 1

1
\$\begingroup\$

This should be interesting. The fastest way I can think of is to pluck all the courses_needed arrays out of Student, group them together, and count them:

course_hash = Student.where.not(courses_needed: nil).pluck(:courses_needed).
 flatten.group_by { |c| c }.map { |k, v| [k, v.size] }.to_h

Essentially, I grab all students that actually have courses_needed, I then grab just all the courses needed from students. Flatten the course arrays so that all courses are within the same array, then I group them by course. After that I map again to replace the values (just the list of all same courses) with the number of times each course is listed. Then I do to_h to turn each [course, number] into a course => number hash that you can access easier.

So pluck returns:

[[course, course2, course3], [course, course2]]

Flatten makes this:

[course, course2, course3, course, course2]

Group_by turns it into:

{course => [course, course], course2 => [course2, course2], course3 => [course3]}

Map does:

[[course, 2], [course2, 2] , [course3, 1]]

Then to_h:

{course => 2, course2 => 2, course3 => 1}

At that point you can access the course hash and get the number.

course_hash[course]
# 2

Now that we have the hash we can update the needed column in Course:

Course.find_each do |course|
 course.update_attributes(needed: course_hash[course].to_i)
end

The to_i is used so that if course_hash does not contain the course it returns nil, then to_i will turn nil into 0.

answered Mar 25, 2016 at 14:48
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.