Trouble sorting a hash (sort of)

Last night I removed my custom tagging system, and converted to acts_as_taggable. I’m thinking DHH’s code is probably a little bit better than mine (just a hunch). I’m also converting my tag methods to use some of the other nice things acts_as_taggable provides, such as: find_tagged_with, tag_names, and one of the reasons for this post, tags_count.

The tags to the left are now being pulled via the tags_count method, but I run into a problem. That method returns a hash where the key is the tag name, and the value is the post count (i.e. [[“rails”, 38],[“personal”, 37], ...]). That’s great and all, but I need to sort it in descending order, based on the value and not the key. So here’s what I’m doing:

1
2
3
def self.get_popular(options={})
  Post.tags_count(:limit => (options[:limit].to_i || 10)).invert.sort.reverse
end

The #invert method of the Hash class essentially swaps the key => value pairs to become value => key pairs. This allows me to call #sort which will then sort by the value (aka the count) rather than the key (aka the name). And at the end of this chain we have #reverse which just gives me the descending order. Seems to work, right? Well, almost.

The problem I’m having is when two tags have the same post count. It seems to then grab only one of them (the first in the alphabet?) I don’t exactly know if this is something I’m worried about fixing, but it makes me feel as though I’m not in control of the code, which I don’t necessarily like. Plus, I don’t know that I fully understand what’s happening, here. It’s ignoring the redundant values in the sort. Thoughts/ideas are welcome, even if it means a new method to get the posts-per-tag count.

Comments

01

Chris on Thu Apr 19 at 04:35AM

Just quickly looking at this, if I recall, invert will return a hash. If two tags have the same count value, and they are inverted, you end up assigning the same key (previously the value) twice. Hash keys are unique, hence you only get one of them in return.

Assuming tags_count returns something like { :tag => count, :tag2 => count }, you can do something like this (untested):

Post.tags_count(..).to_a.sort { |a,b| a[1] <=> b[1] }

And if you need them in the opposite order, make the comparison b[1] <=> a[1] or use .reverse after the block.

02

Ryan on Thu Apr 19 at 05:36AM

You are absolutely correct. I was indeed duplicating keys (not sure why that didn’t cross my mind).

And the untested code in your comment works just like I need (but I removed the .to_a since .sort converts the hash to an array, anyway). Thanks for clearing that up.

03

Chris on Thu Apr 19 at 06:24AM

Yeah, I put to_a just to be safe, but I was pretty sure sort would convert it for you. Glad it worked out ;-)

Have something to say?
Please rewrite the image text Are You Human? Hint: Are You Human? Formatting Tips

or

© 2009 Ryan Heath | Site Management A Ruby on Rails production.

Get in Touch