I’ve been using Ruby a lot lately, and recently had to implement a “related” objects feature in Rails. You know, “Related Videos”, “Related Pages”, etc. I decided to sort them by objects that have the most common tags. Try this one-liner:
#Inside video.rb Model, Related videos def related(count=5) tags.collect { |tag| tag.videos }.flatten.uniq.sort_by { |video| (video.tags & tags).length }.delete_if { |video| self == video }.reverse[0..count-1] end
Let’s try dissecting that:
tags.collect { |tag| tag.videos }
Collects all videos associated with the current object’s tags into an array of arrays (each array representing the slideshows for a particular tag).
.flatten.uniq
Flatten that array (make it a single dimensional array, preserving logical order) and then eliminate duplicates
.sort_by { |video| (video.tags & tags).length }
sort_by sorts the arrays by a particular attribute. So for each video in the array, video.tags & tags
gives the intersection of tags for the video under consideration, and the current object’s tags. Because of .length
, the sort_by function sorts by the number of such intersecting tags.
.delete_if { |video| self == video }
And, remove self references.
.reverse
Sorting is by default ascending, so reverse the array, and:
[0..count-1]
return only the first n elements.
So, you’d call it by something like video = Video.find(1)
and @related_videos = video.related
.
The interesting thing is that, I never ever was a programmer given to verbose lengths of code like this. Doing this in any other language would feel so clunky. But in Ruby, it somehow feels natural. Like you can read through the code in your head and have it make perfect sense.
Leave a Reply