Sorry if this has come up before - I promise I've tried to find it.
My rails app compares a user's football score predictions against actual football scores. If the result is right (eg Barcelona win), you get 1 point. If the result AND score are right (Barcelona win 3-0), you get three points.
The 'if' logic etc is all fine, but I'm struggling to access the 'points' record in my user model. Below is what I have in the controller - any idea what should go in the lines where I'm assigning points to the user (highlighted below)?
This is just an excerpt and the index is there for something separate, so please don't be sidelined too much by that - I'm just trying to work out what replaces the two highlighted lines. All advice welcome!
Predictions controller
def update scores
Prediction.joins(:user).each_with_index do |p, :|
if p.predictionarray[0] == Leagueresult.last.resultarray[0] && p.predictionarray[1..2] == Leagueresult.last.resultarray[1..2]
User.points = User.points + 3 <<<<< What goes on this line?
elsif p.predictionarray[0] == Leagueresult.last.resultarray[0] && p.predictionarray[1..2] != Leagueresult.last.resultarray[1..2]
User.points = User.points + 1 <<<<< And this line?
end
end
You are trying to reference the User-model, instead of a User-classed object. It seems that the user-object you are looking for would be p.user.
Related
I have a model Channel. The relating table has several column, for example clicks.
So Channel.all.sum(:clicks) gives me the sum of clicks of all channels.
In my model I have added a new method
def test
123 #this is just an example
end
So now, Channel.first.test returns 123
What I want to do is something like Channel.all.sum(:test) which sums the test value of all channels.
The error I get is that test is not a column, which of course it is not, but I hoped to till be able to build this sum.
How could I achieve this?
You could try:
Channel.all.map(&:test).sum
Where clicks is a column of the model's table, use:
Channel.sum(:clicks)
To solve your issue, you can do
Channel.all.sum(&:test)
But it would be better to try achieving it on the database layer, because processing with Ruby might be heavy for memory and efficiency.
EDIT
If you want to sum by a method which takes arguments:
Channel.all.sum { |channel| channel.test(start_date, end_date) }
What you are talking about here is two very different things:
ActiveRecord::Calculations.sum sums the values of a column in the database:
SELECT SUM("table_name"."column_name") FROM "column_name"
This is what happens if you call Channel.sum(:column_name).
ActiveSupport also extends the Enumerable module with a .sum method:
module Enumerable
def sum(identity = nil, &block)
if block_given?
map(&block).sum(identity)
else
sum = identity ? inject(identity, :+) : inject(:+)
sum || identity || 0
end
end
end
This loops though all the values in memory and adds them together.
Channel.all.sum(&:test)
Is equivalent to:
Channel.all.inject(0) { |sum, c| sum + c.test }
Using the later can lead to serious performance issues as it pulls all the data out of the database.
Alternatively you do this.
Channel.all.inject(0) {|sum,x| sum + x.test }
You can changed the 0 to whatever value you want the sum to start off at.
I have looked around for info on how to do this, just can't quite get it myself. Fairly new to Ruby and I'm building a gem. I can return a list of results from a website ok with Nokogiri.
The issue I'm having is how to take an input number and relate it to another piece of text from the website I'm scraping. So you pick a movie title by number from a list and then you return the relevant movie outline. Maybe I don't want to iterate through all the objects (again) at all.
This is what I'm looking at:
def menu
input = nil
while input != "exit"
input = gets.strip.downcase
if input.to_i < 24
#movies.each.with_index(1) do |movie, i|
puts "Description: #{movie.outline}"
end
end
end
end
So we create a variable for input. While the input is not the word exit and less than the number 24, we iterate through the movies and put the relevant one by index number. Currently putting ALL the movie outlines so I feel like I should ditch the iteration. I've tried a number of things around adding the input to movie.outline...
Any help or hints would be great!
It's hard to say for sure, but, are you maybe just trying to do this?
input = gets.to_i
if input < 24
movie = #movies[input]
puts "Description: #{movie.outline}"
end
Or something to that effect?
If you want to access an Array element, you use the [] notation (I'm assuming #movies is an Array, but if it's some other enumerable, you'll need to tell us what that is).
This is my first attempt at learning Ruby, and I'm almost there.. this is working except for one piece.. in the function pc_draw the script asks the user if they have a card. If they do then they should be removed from both the "computer's" hand as well as the users hand.. But some reason its not.. Any idea why?
Love to hear some feedback on the code in general as well, any tips or ways to write more concisely or efficiently??
Thanks! and here is the code (edited to just include the relevant code..
def pc_draw # this is the PC playing, id dups, if none then draw, if some then delete and draw..
#first I have to identify the duplicates..
dup = #pc_cards.detect {|e| #pc_cards.rindex(e) != #pc_cards.index(e) }
if dup == nil # if no dups exist then PC has to ask for a card..
#ask_card = rand(#pc_cards.length) # assigns a random number limited to the length of the array
puts "#{#name} do you have a #{#pc_cards[#ask_card]}? Yes or No? " # ask for a random card in the array..
#user_answer = $stdin.gets.chomp
pc_gofish(#user_answer, #ask_card)
else #if there are dups then take them out of the array.
#pc_cards.delete(dup)
#ask_card = rand(#pc_cards.length) # assigns a random number limited to the length of the array
puts "#{#name} do you have a #{#pc_cards[#ask_card]}? Yes or No? " # ask for a random card in the array..
#user_answer = $stdin.gets.chomp
pc_gofish(#user_answer, #ask_card)
end
end
def pc_gofish(take, card_delete) #this will deal with the PC asking the user for cards
if take == "yes" # if the user enters in that their is a match we delete it from pc & user array
#pc_cards.delete(card_delete)
#kates_cards.delete(card_delete)
puts "The computer has #{#pc_cards.length} cards left!" #lets user know how many cards the PC has left
if #pc_cards.length == 0 # if the array is empty then the pc won!
puts "The computer won!! Sorry!"
else #pc_cards.length != 0 # if the array isn't empty, turn reverts back to user.
draw
end
else #this is when there is no match.. so PC has to take a card..
pc_random = rand(#cards.length) # this assigns a random number constrained to the length of the cards remaining
#pc_cards << pc_random # this inserts that random card into the users ask
#cards.delete(pc_random) # this takes the card from the deck of cards..
puts "The computer has #{#pc_cards.length} cards left!" #lets user know how many cards the PC has left
draw
end
end
It looks to me like your detect block is only going to return the first value of the first duplicated item; if you have more than one duplicate, the other duplicates won't be removed.
Eg:
a = [1,2,2,3,4,5,5,5,6]
dups = a.detect{|e| a.rindex(e) != a.index(e)} # => returns 2, but not 5
I don't know how you've modeled your cards, but you could possibly just uniq the array if you're concerned that duplicates exist:
#pc_cards.uniq! # deletes non-uniq elements from the array.
Update: I re-read your question, I see you're asking about the computers cards, which I assume are kept in #cards? Ultimately, I need to agree with the comments above: you have too much code here, and you aren't clearly saying which part is failing.
I would look into either debugger or pry to inspect your code as it runs, or simply put some puts statements into your code so you can see exactly what is really happening at each point.
Try using delete! Instead of delete.
I have a Mongoid model, and I'd like to order the results by a score that's calculated between the model and current_user in real time. Suppose Thing has an instance method match_score:
def match_score(user) #can be user object too
score = 100
score -= 5 if user.min_price && !(price < user.min_price)
score -= 10 if user.max_price && !(price > user.max_price)
#... bunch of other factors...
return [score, 0].max
end
Is it possible to sort the results of any query by the value returned for a particular user?
MongoDB doesn't support arbitrary expressions in sorting. Basically, you only can specify a field and a direction (asc/desc).
With such complicated sorting logic as yours, the only way is to do it in the app. Or look at another data store.
I don't know if there is an easy way to do it but basically what I would like to do is:
var highlights = db.Banners.Where(h => h.Category == "highlight").ToList().GetRange(0,4);
I have this model Banners where I have some highlights but I would like to retrieve just 4 random highlights each time in different order.
So the code I'm using just retrieve a range from [0..4] highlights and if you have less than 4, it returns an error, and they are not randomised.
Any ideas on how could I do it easily?
The result I would like to have is a List<Banner> to pass it to the view but each time with different order like:
[1,3,4,2] || [2,1,4,3] || [12,32,15,3]
I think that's it :)
Thanks a lot
To randomize banners and get first four or less you could do this:
Random r = new Random(DateTime.Now.Ticks);
var highlights = db.Banners.Where(h => h.Category == "highlight").
OrderBy(h => r.Next()).Take(4)
Here is an example of Random LINQ sampling on codeproject