0

I want to display a couple records randomly. First thing I tried was this:

thing1 = Thing.offset(rand(Thing.count)).first
thing2 = Thing.offset(rand(Thing.count)).first

But that sometimes returns duplicates obviously. I guess not much of a problem with a lot of records, but now I'm curious. So I tried this:

random1 = rand(count)
random2 = rand(count)
until random1!=random2 do
  random2 = rand(count)
end
thing1 = Thing.offset(random1).first
thing2 = Thing..offset(random2)).first

But that's not perfect either, if you get, say, 4 and 5 as your random numbers, and record 4 was deleted, you still end up with a duplicate, because offset. Plus, and this is nitpicking, records directly after n deleted records are (n+1) times more likely to come up in this "random" selection.

What's the best way to get multiple unique random records?

NathanTempelman
  • 1,301
  • 9
  • 34

3 Answers3

1

If it will always be two records, you can simply use:

(thing1, thing2) = Thing.order("RANDOM()").limit(2)

To generalize for any number of records, you could make a function for it:

class Thing < ActiveRecord::Base
  def self.random_n_record(n)
    order("RANDOM()").limit(n)
  end
end

And then, for you example above, to get random elements for thing1 and thing2

(thing1, thing2) = Thing.random_n_records(2)
things = Thing.random_n_records(10)

NOTE: If you're using Mysql, replace "RANDOM()" with "RAND()". "RANDOM()" is for Postgresql.

Joe Kennedy
  • 9,365
  • 7
  • 41
  • 55
  • Wouldn't that only return ordered things? record 4, record 5, etc? – NathanTempelman Jun 13 '14 at 21:55
  • I think you might be right. Either way, I updated my answer to use random ordering from the database. – Joe Kennedy Jun 13 '14 at 22:00
  • Ah, that should do it. It'd be a pretty slow query though, wouldn't it? I'm wondering whose solution I should use. – NathanTempelman Jun 13 '14 at 22:03
  • Good question. I'm not sure. You could test them both, and see which seems to work better for your use case. On a giant database, your solution would probably be quicker, as finding a random number in ruby at most two or three times (realistically) would probably be faster than randomly ordering thousands or millions of rows. – Joe Kennedy Jun 13 '14 at 22:06
  • Yeah, I'm thinking at least a couple thousand. Anyways, thanks for the help. And nice website, probably the bro-est thing I've ever seen on the internet. And I once watched a man chop a tree down with an axe tied to his beard. – NathanTempelman Jun 13 '14 at 22:12
  • Thanks! And that sounds awesome. That would make for a great video of the day. – Joe Kennedy Jun 13 '14 at 22:19
1
count = Thing.count
thing1 = Thing.offset(rand(count)).first
thing2 = Thing.offset(rand(count)).first
until thing1.id != thing2.id do
  thing2 = Thing.offset(rand(count)).first
end

Got it.

NathanTempelman
  • 1,301
  • 9
  • 34
0

You cannot guarantee uniqueness if you're picking records simultaneously. If you need the records to be unique, you will have to randomly pick a record, then exclude it from the set of records, pick another randomly, etc. etc. This problem should be implemented easily using recursion.

indiscrete
  • 79
  • 4