Dudo
Dudo

Reputation: 4169

How do I return a unique random row from a table in Ruby?

model

class Answer < ActiveRecord::Base
def self.energy(v, w)
  a = self.where('energy_id = ? AND weight = ?', v, w)
  a.offset(rand(a.count)).first.name
end

view

<%= form_for(@answer) do |f| %>
  <%= f.submit "#{Answer.energy(3, 1)}", name: "answer", class: "btn" %>
  <%= f.submit "#{Answer.energy(4, 1)}", name: "answer", class: "btn" %>
<% end %>

I have that, and it returns a random value, properly. I'm calling this 36 times though (18 pairs of 2), and I don't want the same value returned more than once, ever. I tried all kinds of .pop variations, but failed every time.

Thanks for the help!

FOR THE CALL, I'm using these form buttons, so I went with:

<% names = [] %>

<div id='one' class='center'>
  <%= form_for(@answer) do |f| %>
    <%= f.submit "#{record = Answer.energy(3, 1, names)}", name: "answer", class: "btn btn-large btn-primary" %>
    <% names << record %>
    <%= f.submit "#{record = Answer.energy(4, 1, names)}", name: "answer", class: "btn btn-large btn-primary" %>
    <% names << record %>
  <% end %>
</div>
~~ 17 more times ~~

works like a charm! Thanks, jvnill, for the help!

Upvotes: 0

Views: 107

Answers (3)

jvnill
jvnill

Reputation: 29599

you can user .order("RAND()") for mysql and .order("RANDOM()") for postgre.

UPDATE: no duplicates.

def self.energy(v, w)
  where('energy_id = ? AND weight = ?', v, w).limit(16).uniq.pluck(:name)
end

UPDATE: 16 times, no duplicate

this returns the first random record that matches the energy and weight and is not included in except_names

# model
def self.energy(v, w, except_names = [])
  klass = where('energy_id = ? AND weight = ?', v, w)
  klass = klass.where('name NOT IN (?)', except_names) if except_names.any?
  klass.order('RAND()').first.name
end

this calls the method 16 times, each time adding a uniq name to names

# how to call
names = []
16.times do
  if record = Model.energe(1, 1, names)
    names << record
  end
end

Upvotes: 2

Iuri G.
Iuri G.

Reputation: 10630

if you are calling it 16 times can you pass the already available rows as argument? if yes than would this work?

def self.energy(v, w, records = [])
  a = self.where("energy_id = ? AND weight = ? AND id NOT IN ('-1',?)", v, w, Array(records).map(&:to_param))
  a.offset(rand(a.count)).first.name
end

Usage:

records = []
16.times do
 records << Answer.energy(10, 5, records)
end

you can also pass single record instead of array:

other_record = Answer.energy(10,5)
Answer.energy(10, 5, other_record)

Upvotes: 1

Leo
Leo

Reputation: 2103

def self.energy(v, w)
  c = []
  a = self.where('energy_id = ? AND weight = ?', v, w)
  b = a.offset(rand(a.count)).first.name
  unless c.include?(b) do
    puts b
    c << b
    end
end

Upvotes: 1

Related Questions