Return all user profile except current users' - ruby-on-rails

I am iterating over all user profiles to get their country. I wish not to include the current user's profile. If I have a total of 10 users, I want only 9 user's country:
# helper function
def method_name
Profile.all.select do |m|
n = m.country.class == String # Because most countries will be nil. A shorter way to not include nil profiles?
return n.reject! {|x| x == current_user.profile.user_id }
end
end
The method above should return all user's profile except for the current user.
The error I got is
undefined method `reject!' for false:FalseClass
The reason? In my views I could:
<%= method_name.map do |p| p.country end %>

Beware of using Profile.all here, because you will potentially use a lot of memory instantiating a profile object for every row in your database.
I think the best way to do this is to create a method within your Profile model, which performs the query, and uses pluck() to return an array containing just the countries you want.
def self.countries_except_for(user)
where.not(user_id: user.id, country: nil).pluck(:country)
end
Now you can just call:
Profile.countries_except(current_user)
To get the array of countries. This approach is much more efficient than looping through as in your question.

The line n = m.country.class == String sets n to true or false, thefore the next line fails, because you try to call reject! {|x| x == current_user.profile.user_id } on that boolean value.
One way to solve the problem might be to fix your code like this:
def method_name
Profile.all.select do |p|
p.country.present? && p != current_user.profile
end
end
Another way - and my preferred one - would be to only load matching record from the database:
def method_name
Profile.where.not(user_id: current_user.id).where.not(country: nil)
end

Related

Refactoring a large method with many conditions - Ruby

I have this method:
method:
def unassigned_workers?(users)
assigned_users = []
unassigned_users = []
users.each do |user|
if user.designated_to_assignment?(self)
assigned_users << user
else
unassigned_users << user
end
end
if unassigned_users.count > 0
true
else
false
end
end
It's in my Assignment model. The assignment model has many Users, and basically what this method is trying to do is check if the user is designated to the assignment based on another relationship I have setup. It checks if the user is assigned and pushes it on the correct array. Does anybody know how I can refactor this to be smaller and more readable?
How about using any?
assigned_users not necessarily required.
def unassigned_workers?(users)
users.any? { |user| !user.designated_to_assignment?(self) }
end
not sure why you have assigned_users at all
try:
def unassigned_workers?(users)
users.reject { |user| user.designated_to_assignment?(self) }.count > 0
end
reject removes elements from a collection that match a predicate.
Moreover passing a self in a model as an argument is a code smell, maybe the dependencies are reversed

Ignore parameters that are null in active record Rails 4

I created a simple web form where users can enter some search criteria to look for venues e.g. a price range. When a user clicks "find" I use active record to query the database. This all works very well if all fields are filled in. Problems occur when one or more fields are left open and therefore have a value of null.
How can I work around this in my controller? Should I first check whether a value is null and create a query based on that? I can imagine I end up with many different queries and a lot of code. There must be a quicker way to achieve this?
Controller:
def search
#venues = Venue.where("price >= ? AND price <= ? AND romance = ? AND firstdate = ?", params[:minPrice], params[:maxPrice], params[:romance], params[:firstdate])
end
You may want to filter out all of the blank parameters that were sent with the request.
Here is a quick and DRY solution for filtering out blank values, triggers only one query of the database, and builds the where clause with Rails' ActiveRecord ORM.
This approach safeguards against SQL-injection, as pointed out by #DanBrooking. Rails 4.0+ provides "strong parameters." You should use the feature.
class VenuesController < ActiveRecord::Base
def search
# Pass a hash to your query
#venues = Venue.where(search_params)
end
private
def search_params
params.
# Optionally, whitelist your search parameters with permit
permit(:min_price, :max_price, :romance, :first_date).
# Delete any passed params that are nil or empty string
delete_if {|key, value| value.blank? }
end
end
I would recommend to make method in Venue
def self.find_by_price(min_price, max_price)
if min_price && max_price
where("price between ? and ?", min_price, max_price)
else
all
end
end
def self.find_by_romance(romance)
if romance
where("romance = ?", romance)
else
all
end
end
def self.find_by_firstdate(firstdate)
if firstdate
where("firstdate = ?", firstdate)
else
all
end
end
And use it in your controller
Venue
.find_by_price(params[:minPrice], params[:maxPrice])
.find_by_romance(params[:romance])
.find_by_firstdate(params[:firstdate])
Another solution to this problem, and I think a more elegant one, is using scopes with conditions.
You could do something like
class Venue < ActiveRecord::Base
scope :romance, ->(genre) { where("romance = ?", genre) if genre.present? }
end
You can then chain those, which would work as an AND if there is no argument present, then it is not part of the chain.
http://guides.rubyonrails.org/active_record_querying.html#scopes
Try below code, it will ignore parameters those are not present
conditions = []
conditions << "price >= '#{params[:minPrice]}'" if params[:minPrice].present?
conditions << "price <= '#{params[:maxPrice]}'" if params[:maxPrice].present?
conditions << "romance = '#{params[:romance]}'" if params[:romance].present?
conditions << "firstdate = '#{params[:firstdate]}'" if params[:firstdate].present?
#venues = Venue.where(conditions.join(" AND "))

removing objects from an array during a loop

I am trying to filter the results of an user search in my app to only show users who are NOT friends. My friends table has 3 columns; f1 (userid of person who sent request), f2 (userid of friend who received request), and confirmed (boolean of true or false). As you can see, #usersfiltered is the result of the search. Then the definition of the current user's friend is established. Then I am trying to remove the friends from the search results. This does not seem to be working but should be pretty straight forward. I've tried delete (not good) and destroy.
def index
#THIS IS THE SEARCH RESULT
#usersfiltered = User.where("first_name LIKE?", "%#{params[:first_name]}%" )
#THIS IS DEFINING ROWS ON THE FRIEND TABLE THAT BELONG TO CURRENT USER
#confirmedfriends = Friend.where(:confirmed => true)
friendsapproved = #confirmedfriends.where(:f2 => current_user.id)
friendsrequestedapproved = #confirmedfriends.where(:f1 => current_user.id)
#GOING THROUGH SEARCH RESULTS
#usersfiltered.each do |usersfiltered|
if friendsapproved.present?
friendsapproved.each do |fa|
if usersfiltered.id == fa.f1
#NEED TO REMOVE THIS FROM RESULTS HERE SOMEHOW
usersfiltered.remove
end
end
end
#SAME LOGIC
if friendsrequestedapproved.present?
friendsrequestedapproved.each do |fra|
if usersfiltered.id == fra.f2
usersfiltered.remove
end
end
end
end
end
I would flip it around the other way. Take the logic that is loop-invariant out of the loop, which gives a good first-order simplification:
approved_ids = []
approved_ids = friendsapproved.map { |fa| fa.f1 } if friendsapproved.present?
approved_ids += friendsrequestedapproved.map { |fra| fra.f2 } if friendsrequestedapproved.present?
approved_ids.uniq! # (May not be needed)
#usersfiltered.delete_if { |user| approved_ids.include? user.id }
This could probably be simplified further if friendsapproved and friendsrequestedapproved have been created separately strictly for the purpose of the deletions. You could generate a single friendsapproval list consisting of both and avoid unioning id sets above.
While I agree that there may be better ways to implement what you're doing, I think the specific problem you're facing is that in Rails 4, the where method returns an ActiveRecord::Relation not an Array. While you can use each on a Relation, you cannot in general perform array operations.
However, you can convert a Relation to an Array with the to_a method as in:
#usersfiltered = User.where("first_name LIKE?", "%#{params[:first_name]}%" ).to_a
This would then allow you to do the following within your loop:
usersfiltered.delete(fa)

How to query many fields, allowing for NULL

I have a Rails site that logs simple actions such as when people upvote and downvote information. For every new action, an EventLog is created.
What if the user changes his or her mind? I have an after_create callback that looks for complementary actions and deletes both if it finds a recent pair. For clarity, I mean that if a person upvotes something and soon cancels, both event_logs are deleted. What follows is my callback.
# Find duplicate events by searching nearly all the fields in the EventLog table
#duplicates = EventLog.where("user_id = ? AND event = ? AND project_id = ? AND ..., ).order("created_at DESC")
if #duplicates.size > 1
#duplicates.limit(2).destroy_all
end
The above code doesn't quite work because if any of the fields happen to be nil, the query returns [].
How can I write this code so it can handle null values, and/or is there a better way of doing this altogether?
If I understood this correctly,
some of the fields can be nil, and you want to find activity logs that have same user_id, same project_id or project id can be nil.
So I guess this query should work for you.
ActivityLog.where(user_id: <some_id> AND activity: <complementary_id> AND :project_id.in => [<some_project_id>, nil] ....)
This way you would get the complementary event logs where user_id is same and project id may or may not be present
class ActivityLog
QUERY_HASH = Proc.new{ {user_id: self.user_id,
activity: complementary_id(self.id),
and so on....
} }
How about:
# event_log.rb
def duplicate_attr_map
{
:user_id,
:project_id
}
end
def duplicates
attribs = duplicate_attr_map.reject_if(&:blank?)
query = attribs.map { |attr| "#{attr} = ?" }.join(' AND ')
values = attribs.map { |attr| self.send(attr) }
EventLog.where(query, *values).order("created_at DESC")
end
def delete_duplicates(n)
duplicates.limit(n).delete_all if duplicates.size > 1
end
# usage:
# EventLog.find(1).delete_duplicates(2)
not tested, could be improved

How to store the result of my algorithm?

I have an algorithm that searches through all of my sites users, finding those which share a common property with the user using the algorithm (by going to a certain page). It can find multiple users, each can have multiple shared properties. The algorithm works fine, in terms of finding the matches, but I'm having trouble working out how to store the data so that later I'll be able to use each unit of information. I need to be able to access both the found users, and each of the respective shared properties, so I can't just build a string. This is an example of the output, being run from the perspective of user 1:
user 4
sharedproperty3
sharedproperty6
user 6
sharedproperty6
sharedproperty10
shareproperty11
What do I need to do to be able to store this data, and have access to any bit of it for further manipulation? I was thinking of a hash of a hash, but I can't really wrap my head around it. I'm pretty new to programming, and Ruby in particular. Thanks for reading!
EDIT - Here's the code. I'm fully expecting this to be the most incorrect way to do this, but it's my first try so be gentle :)
So if I'm understanding you guys correctly, instead of adding the interests to a string, I should be creating an array or a hash, adding each interest as I find it, then storing each of these in an array or hash? Thanks so much for the help.
def getMatchedUsers
matched_user_html = nil
combined_properties = nil
online_user_list = User.logged_in.all
shared_interest = false
online_user_list.each do |n| # for every online user
combined_properties = nil
if n.email != current_user.email # that is not the current user
current_user.properties.each do |o| # go through all of the current users properties
n.properties.each do |p| # go through the online users properties
if p.interestname.eql?(o.interestname) # if the online users property matches the current user
shared_interest = true
if combined_properties == nil
combined_properties = o.interestname
else
combined_properties = combined_properties + ", " + o.interestname
end
end
end
if shared_interest == true
matched_user_html = n.actualname + ": " + combined_properties
end
end
end
end
return matched_user_html
render :nothing => true
end
This returns an array of hashes with all users and their corresponding sharedproperties.
class User
def find_matching_users
returning Array.new do |matching_users|
self.logged_in.each do |other_user|
next if current_user == other_user # jump if current_user
# see http://ruby-doc.org/core/classes/Array.html#M002212 for more details on the & opreator
unless (common_properties = current_user.properties & other_user.properties).empty?
matching_users << { :user => other_user, :common_properties => common_properties }
end
end
end
end
end
In your view you can do something like this:
<%- current_user.find_matching_users.each do |matching_user| -%>
<%-# you can acccess the user with matching_user[:user] -%>
<%-# you can acccess the common properties with matching_user[:common_properties] -%>
<%- end -%>
You can use a hash table with the key being the user object and the value being an array of the shared properties . This is assuming that you first need to do a lookup based on the user .
Something like this :
#user_results = { user1 => [sharedproperty3,sharedproperty7] , user2 => [sharedproperty10,sharedproperty11,sharedproperty12]}
You can then acces the values like :
#user_results[user1]
or you can also iterate over all the keys using #user_results.keys

Resources