3
\$\begingroup\$

I am using the ancestry gem to organize my post categories tree. I have 8 parent categories, and they all have many child categories. To retrieve an array of child categories, I use:

parent_category.children

I defined these relations in my category model:

  has_ancestry
  has_many :posts
  has_many :users, through: :posts

The last relation allows me to find the users who post in each category.

What I am trying to do: on first login, users have to choose their categories of interest. On the next screen, they are suggested users who post in the categories they chose.

To find the suggested users, I wrote a method:

def recommendations
    @users = []
    (current_user.get_voted Category).each do |category|
      category.children.each do |cat|
        @users << cat.users
      end
    end
    @users = @users.flatten.uniq
 end

This works, but it seems overly complicated. Is there any way to do this in a "best practice" way ?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Some notes:

  • ys = [] + xs.each + ys.push + ys.flatten That's an imperative and pretty verbose pattern. A functional approach would use flat_map.
  • @users = @users.flatten.uniq. Don't re-use variables. Different values, different variables.
  • (current_user.get_voted Category). You saved two parens on the function call at the cost of being forced to write them to wrap the expression. It's not worth it. I'd just use parens on calls except in DSL-style statements. Check https://github.com/bbatsov/ruby-style-guide

I'd write:

def recommendations
  @users = current_user.get_voted(Category).
    flat_map { |category| category.children.flat_map(&:users) }.
    uniq
end
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the help, especially for the detailed explanations. To have the exact same result, I had to change .map to .flat_map in the block, but otherwise it's great \$\endgroup\$ Commented Mar 17, 2016 at 15:10
  • 1
    \$\begingroup\$ I was just editing it, you need another flattening there, indeed. Glad to help! A hint: usually it's a good idea to leave some time for others to respond before accepting an answer. \$\endgroup\$
    – tokland
    Commented Mar 17, 2016 at 15:11

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.