Rails get unique country with cities - ruby-on-rails

How can I list unique country followed by related cities? Following were my product table:
name country city
p1 US New York
p2 US Boston
p3 US Chicago
k1 UK London
k2 UK Liverpool
Controller:
#countries = Product.joins(:user).distinct.where("country is not null and country <> ''").where(:users => {:merchant_status => 1}).pluck(:country)
#cities = Product.joins(:user).distinct.where("city is not null and city <> ''").where(:users => {:merchant_status => 1}).pluck(:city)
#countries.map! {|country| country.split.map(&:capitalize).join(' ')}
#search_location_country = #countries
And in my View:
<ul id="color-dropdown-menu" class="dropdown-menu dropdown-menu-right" role="menu">
<% #search_location_country.each do |country| %>
<li class="input"><%= country %></li>
<% end %>
</ul>
How can I sort the end result for drop down like this:
US
- New York
- Boston
- Chicago
UK
- London
- Liverpool
Thanks!!
EDIT
To be display something like this:

Hey you can try this way using group it gives you all distinct records
#countries_cities = Product.joins(:user).where("country is not null and country <> ''").where("city is not null and city <> ''").where(:users => {:merchant_status => 1}).group(:country, :city).select("country,city").as_json
It will give you output like
[{:country => "US", :city => "New York"}..]
If you want to again group it by country then used like
cchs = #countries_cities.group_by{|cc| cc["country"]}
Convert above multidimensional array to hash using
#country_cities_hash = = Hash[*cchs]
In your view file as
<% #country_cities_hash.each do |country, cities| %>
<li class="input"><%= country %></li>
<% cities.each do |city| %>
<li class="input"><%= "#{city}(#{country})" %></li>
<% end %>
<% end %>

Not sure I do understand the question but... I guess you have a collection of Product, looking like this :
produts = [
<Product #name="p1", #country="US" #city="New York">,
<Product #name="p1", #country="US" #city="Boston">,
<Product #name="k2", #country="FR" #city="Paris">,
...
]
In that case, to index the city names by country :
#cities_by_coutry = products.inject({}) do |index, product|
index[product.country] ||= []
index[product.country] << product.city
index
end
Which result to :
{"US"=>["New York", "Boston"], "FR"=>["Paris"]}
Then you can iterate :
#cities_by_coutry.each do |country, cities|
cities.each do |city|
puts "City: #{city} is in country {country}"
end
end

Related

How to Make a TODO list item when a database cell matches today's date

I'd like to create a todo list looking for any cell that matches today's date on either agreement, start or due date.
<% #project.where("project_date_agreement + project_date_start + project_date_due > ?", Date.today).each do |tasks_today| %>
<ul>
<li>Item Due Today</li>
</ul>
<% end %>
Any help getting me in right direction would be appreciated.
You want to use OR conditions in your where clause. Properly speaking this would be in your controller.
#projects = Project.where('date_agreement = ? OR date_start = ? or date_due = ?', Date.today, Date.today, Date.today)
In your Project Model you might want to create a field that say's what's due...
def due_because
due_array = []
due_array << 'Agreement date' if date_agreemnt == Date.today
due_array << 'Start date' if date_start == Date.today
due_array << 'Due date' if date_due == Date.today'
due_array.join(', ')
end
Then in your view you would iterate over the #projects
<ul>
<li>Items Due Today</li>
<% #projects.each do |project| %>
<li><%=project.name%> <%=project.due_because%></li>
<% end %>
</ul>
if you asking one row and different column, how about if you combine with table
<% #tasks = #projects.where("date_agreement = ? AND date_start = ? AND date_due = ?",Date.today ,Date.today, Date.today) %>
<table>
<tr>
<ul>
<% #tasks.each do |task| %>
<td>
<li><%= task.check_box :item_due %></li>
</td>
<% end %>
</ul>
</tr>
</table>

Ruby on Rails sort on priorities

Following were my query to get list of countries along with cities:
#countries_cities = Product.joins(:user).where("country is not null and country <> ''").where("city is not null and city <> ''").where(:users => {:merchant_status => 1}).group(:country, :city).select("country,city").as_json
The output result were as follow:
Object[city:"Bangkok",country:"Thailand"],Object[city:"Phuket",country:"Thailand"],Object[city:"Malaysia",country:"Kuala Lumpur"],Object[city:"Malaysia",country:"Penang"],Object[city:"Shanghai",country:"China"],Object[city:"Beijing",country:"China"]
cchs = #countries_cities.group_by{|cc| cc["country"]}
#search_location_country = cchs
And the view is:
<ul id="color-dropdown-menu" class="dropdown-menu dropdown-menu-right" role="menu">
<% #search_location_country.each do |country, cities| %>
<li class="input" style="background:#ECECEC; "><%= country.upcase %></li>
<% cities.each do |city| %>
<li class="input"><%= city["city"].titleize %></li>
<% end %>
<% end %>
</ul>
Now the Drop down result follow this pattern:
Thailand
-Bangkok
-Phuket
Malaysia
-Kuala Lumpur
-Penang
China
-Beijing
-Shanghai
How can I ensure that Malaysia will always place at the top of the drop down lists? Thanks!!
How about:
#countries_cities = Product.joins(:user)
.where.not(country: [nil, ''])
.where(users: {merchant_status: 1})
.group(:country, :city)
.order("country!= 'Malaysia'")
.select(:country, :city)
.as_json
In Postgres, false is sorted before true (see this answer here: Custom ORDER BY Explanation)
You can customize you query like this:
#countries_cities = Product.joins(:user)
.where.not(country: [nil, ''])
.where(:users => {:merchant_status => 1})
.group(:country, :city)
.order("ORDER BY
CASE WHEN country = 'Malaysia' THEN 1
ELSE 2
END ASC")
.select(:country, :city)
.as_json
So we set the order of Malaysia = 2, and others = 1 to ensure the result with Malaysia will be on the top.

How to determine the order from a controller

I have a restaurant with many employees and each employee has many customer ratings.
I want to create a stats page that shows the employees ordered by their monthly ratings average.
In the employee model:
def avg_rating
date = Date.today
ratings_total = self.ratings.sum(:score, :conditions => {:created_at => (date.beginning_of_month..date.end_of_month)}).to_f
ratings_count = self.ratings.count(:conditions => {:created_at => (date.beginning_of_month..date.end_of_month)}).to_f
return (ratings_total/ ratings_count)
end
In the restaurant controller I have:
def restaurant_stats
#restaurant = Restaurant.find(params[:restaurant_id])
#employees = #restaurant.employees.all
end
In the restaurant stats view:
<table>
<% #employees.each do |employee| %>
<tr>
<td><%= employee.name %></td>
<td><%= employee.avg_rating %></td>
</tr>
<% end %>
</table>
I'm not sure how to get the employees in the correct order? I assume I would have to retrieve the values in the correct order in the restaurant_stats action instead of just #restaurant.employees.all but I'm not sure how to because of the functions used in the employees model
You could do, from the controller:
#employees = #restaurant.employees.all.sort_by {|employee| employee.avg_rating}
or more concisely
#employees = #restaurant.employees.all.sort_by(&:avg_rating)
Note that this will load all employees in memory for sorting.
Try in the restaurant controller:
#employees = #restaurant.employees.all.sort {|x,y| y.avg_rating <=> x.avg_rating }
or
#employees = #restaurant.employees.all.sort_by(:avg_rating)
you could create an array and sort it
I think below works but haven't checked it
#employees = #restaurant.employees.collect {|p| [ p.name, p.avg_rating ]}
#employees.sort!{ |a,b| (a[1] <=> b[1]) }

What is the proper way to form this multi-dimensional array?

I am attempting to perform a series of requests that will organize the results in a given date span in a way where each location will live on the top level, and contain an another array with each set of results. Here is what I have so far:
locations = Location.all
#requests = []
locations.each do |location|
request = PurchaseRequest.where('created_at >= ? AND created_at <= ? AND location_id = ?', params[:start_date], params[:end_date], location.id).order(:location_id)
#requests.push(location => request)
end
My ideal (non-working) implementation in the view would look something like:
<ul>
<% #requests.each do |location| %>
<li>location[0].name</li>
<ul>
<% location[1].each do |request| %>
<li>request.name</li>
<% end %>
</ul>
<% end %>
</ul>
Try something like this:
#requests = {}
Location.all.each do |location|
#requests[location.name] = PurchaseRequest.where('created_at >= ? AND created_at <= ? AND location_id = ?', params[:start_date], params[:end_date], location.id).order(:location_id)
end
and:
<ul>
<% #requests.each do |location_name, request_list| %>
<li><%= location_name %></li>
<ul>
<% request_list.each do |request| %>
<li><%= request.name %></li>
<% end %>
</ul>
<% end %>
</ul>
#requests becomes a hash with the key being the location name and the value being the purchase requests.

this query isn't efficient

i have a single search field that is querying three different columns from two different tables: "companies" and "industries" from a positions table and "schools" from an educations table. it is successfully returning all users that meet ALL fields entered into the search field (using select_tag). this is from my view:
<%= form_tag(search_path, :method => :get, :id => "people_search") do %>
<div class="row-fluid">
<div class="span4">
<table>
<tr>
<td>
<div class="search-table" style="padding-left:55px">
<%= select_tag "all", options_for_select((#visible_companies + #visible_industries + #visible_schools).uniq, params[:all]), { :placeholder => "Search by companies, industries and schools...", :multiple => true, :js => "if (evt.keyCode == 13) {form.submit();}" } %>
</div>
</td>
<td>
<%= submit_tag "Add", id: "send-button", style:"width:175px;" %>
</td>
</tr>
</table>
</div>
<% end %>
</div>
and controller:
#visible_positions = Position.where{ is_visible.eq('true') }
#visible_educations = Education.where{ is_visible.eq('true') }
#visible_companies = #visible_positions.order("LOWER(company)").map(&:company).uniq
#visible_industries = #visible_positions.order("LOWER(industry)").map(&:industry).uniq
#visible_schools = #visible_educations.order("LOWER(school)").map(&:school).uniq
#c = #visible_positions.where{company.in(my{params[:all]})}.map(&:user_id)
#i = #visible_positions.where{industry.in(my{params[:all]})}.map(&:user_id)
#s = #visible_educations.where{school.in(my{params[:all]})}.map(&:user_id)
#C = #visible_positions.where{company.in(my{params[:all]})}.map(&:company)
#I = #visible_positions.where{industry.in(my{params[:all]})}.map(&:industry)
#S = #visible_educations.where{school.in(my{params[:all]})}.map(&:school)
#blacklist = []
#cis = #c + #i + #s
#experiences = ([#C,#I,#S].reject(&:empty?).reduce(:&))
#cis.uniq.each do |user_id|
unless #C.empty?
#C.uniq.each do |company|
unless Position.find_all_by_company(company).map(&:user_id).include?(user_id) || Position.find_all_by_industry(company).map(&:user_id).include?(user_id) || Education.find_all_by_school(company).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #I.empty?
#I.uniq.each do |industry|
unless Position.find_all_by_industry(industry).map(&:user_id).include?(user_id) || Position.find_all_by_company(industry).map(&:user_id).include?(user_id) || Education.find_all_by_school(industry).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #S.empty?
#S.each do |school|
unless Education.find_all_by_school(school).map(&:user_id).include?(user_id) || Position.find_all_by_company(school).map(&:user_id).include?(user_id) || Position.find_all_by_industry(school).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
end
unless #c.empty? && #i.empty? && #s.empty?
#users = User.find(#cis - #blacklist)
end
the search looks like this (notice the single field), with a sample query included (notice the AND filter...i'm the only user in the database that fits all search terms ['dartmouth college' for school, 'world health organization' for company, 'internet' for industry]):
i realize this is not an efficient query and am thinking of ways to speed it up, but could use some ideas at this point.
happy turkey day :)
Based on your description rather then on understanding your code I figured out something like this
User.joins(:positions, :educations).where("lower(positions.company) like lower(?) and lower(positions.industry) like lower(?) and lower(educations.school) like lower(?) and positions.is_visible and educations.is_visible", "%#{company}%", "%#{industry}%", "%#{school}%")
or if there is only one company or industry in column
User.joins(:positions, :educations).where("(lower(positions.company) = lower(?) or lower(positions.industry) = lower(?)) and lower(educations.school) = lower(?) and positions.is_visible and educations.is_visible", company,industry, school)
But to put many industries, companies, schools as params will be more complicated
and create indexes
create index positions_lower_company on positions (lower(company));
create index positions_lower_industry on positions (lower(industry));
create index educations_lower_school on educations (lower(school));
I hope it will help somehow.

Resources