For my application(Ruby on Rails) i have country select box for the signup page. These countries are localized into different language. But i couldnt find a way to sort them, based on the language in which its localized. At present i have sorted it out based on english only. Is there a way to sort the country names based on the locale? i.e the order of the countries should change(ascending order) according to the language its localised.
Thanks..
You can make a custom String comparison method, based on a given alphabet, something like this (works in Ruby 1.9):
class String
# compares two strings based on a given alphabet
def cmp_loc(other, alphabet)
order = Hash[alphabet.each_char.with_index.to_a]
self.chars.zip(other.chars) do |c1, c2|
cc = (order[c1] || -1) <=> (order[c2] || -1)
return cc unless cc == 0
end
return self.size <=> other.size
end
end
class Array
# sorts an array of strings based on a given alphabet
def sort_loc(alphabet)
self.sort{|s1, s2| s1.cmp_loc(s2, alphabet)}
end
end
array_to_sort = ['abc', 'abd', 'bcd', 'bcde', 'bde']
ALPHABETS = {
:language_foo => 'abcdef',
:language_bar => 'fedcba'
}
p array_to_sort.sort_loc(ALPHABETS[:language_foo])
#=>["abc", "abd", "bcd", "bcde", "bde"]
p array_to_sort.sort_loc(ALPHABETS[:language_bar])
#=>["bde", "bcd", "bcde", "abd", "abc"]
And then provide alphabetical orders for every language you want to support.
Sometime ago twitter released a library which can nicely take care of it in Ruby for multiple language and it actually works https://github.com/twitter/twitter-cldr-rb#sorting-collation . It is also very nice that they provided a higher level way for sorting as well as low-level which can just compare two string for given locale. This allowed me to get rid of git://github.com/k3rni/ffi-locale.git which I was using so far for sorting string in a locale-aware way.
Maybe you can translate all and sort it after this translation.
Related
I have a method in a model with Rails. This is the code:
def ensure_appropriate_age
age = match[:age]
if age[/\d/].to_i > 18
current_user.update age: age[/\d/].to_i
else
%{We could not proceed, sorry.}
end
end
I'm taking the input of what the user types and if it contains a number, verify that it is greater than 18 to store it.
I'll enter my age like so: im 24. I still get false, and I can see in the database it's only storing the number "2".
What is required to make this work?
You have to use \d+ to match multiple digits; \d only matches a single digit in a number.
You can also capture the value of the regex into a variable, so that you don't have to evaluate it twice, like shown:
def ensure_appropriate_age(age_string)
age = age_string[/\d+/].to_i
if age > 18
current_user.update age: age
else
%{We could not proceed, sorry.}
end
end
It's good to step back a second, and ask yourself why do you need to extract the age from a string here. What else are you holding in it? If more information is present, build a quick parser in the caller or initializer to partition the data, and cast to the proper types.
The trick is: once the age is isolated from the other information, casting is straightforward.
user_data = "John Smith 22"
#name, #surname, age_str = user_data.split(" ")
#age = age_str.to_i
Then just use your function. Ruby style guides also advise you to use quotes "..."rather than
%{} unless needed (i.e. your string is full of quotes).
If you push yourself to keep it simple whenever you can, you will find your code easier to read and faster to write and debug. There's plenty cases in which regex(es?) are the simplest way to go, but until you get there don't sweat it :)
So I have a nested activerecord which contains an array of hashes. I am trying to get the country in an app I am making using a country code that is stored in one of the elements in the array.
the record is described:
user.rules.first.countries.first["country_code"]
user has_many rules,
rules contains a jsonb column called countries
countries is a jsonb array of hashes
at the moment I am iterating through all of them to find the record. e.g.
country_code_to_find = "US"
user.rules.each do |r|
r.countries.each do |c|
if c["country_code"] == "US"
# Do some stuff
end
end
end
Is there a way I can access that country with a single line using a .where() or scope or something like that? I am using rails 4, activerecord and postgres.
Without knowing more about the JSON structure, I'm not confident you can access "that country" with a single query, since a "country" is an element in an array. You can query for the Rule objects that contain the desired "country". Something like this might work
user.rules.where("
countries #> '[{\"country_code\": \"US\"}]'
")
Depending on your business logic, it might be enough to know that this user has at least one rule with country=US.
country_code_to_find = "US"
if user.rules.where("countries #> '[{\"country_code\": \"#{country_code_to_find}\"}]'").exists?
# Do some stuff
end
More on Postgres' JSONB functions.
These questions seem related, but are not Rails-specific:
Postgresql query array of objects in JSONB field.
Query for array elements inside JSON type
Using the answer from messenjah I was able to get a solution that worked. Had to find the index of the array so I could use it. To give some more information that messenjar was after here is the json:
countries: [{"code"=>"US", "name"=>"United States", "states"=>{"NY" => "New York"}}, {"code"=>"MX", "name"=>"Mexico", "states"=>{"YUC" => "Yucatán"}}]
Then to get an array of the states I used:
country_code = "MX"
rule = #user.rules.where("countries #> '[{\"code\": \"#{country_code}\"}]'").first
country_index = rule.countries.index {|h| h["code"] == country_code }
states = rule.countries[country_index]["states"]
Basically this get the index of the array of hashes that I want. Not sure if this is better or worse than what I was using to begin with. But it works. Happy to consider other answers if they can clean this up.
I have a web page where a user can search through documents in a mongoDB collection.
I get the user's input through #q = params[:search].to_s
I then run a mongoid query:
#story = Story.any_of( { :Tags => /#{#q}/i}, {:Name => /#{#q}/i}, {:Genre => {/#{#q}/i}} )
This works fine if the user looks for something like 'humor' 'romantic comedy' or 'mystery'. But if looking for 'romance fiction', nothing comes up. Basically I'd like to add 'and' 'or' functionality to my search so that it will find documents in the database that are related to all strings that a user types into the input field.
How can this be done while still maintaining the substring search capabilties I currently have?Thanks in advance for help!
UPDATE:
Per Eugene's comment below...
I tried converting to case insensitive with #q.map! { |x| x="/#{x}/i"}. It does save it properly as ["/romantic/i","/comedy/i"]. But the query Story.any_of({:Tags.in => #q}, {:Story.in => #q})finds nothing.
When I change the array to be ["Romantic","Comedy"]. Then it does.
How can I properly make it case insensitive?
Final:
Removing the quotes worked.
However there is now no way to use an .and() search to find a book that has both words in all these fields.
to create an OR statement, you can convert the string into an array of strings, and then convert the array of strings into an array of regex and then use the '$in' option. So first, pick a delimeter - perhaps commas or space or you can set up a custom like ||. Let's say you do comma seperated. When user enters:
romantic, comedy
you split that into ['romantic', 'comedy'], then convert that to [/romantic/i, /comedy/i] then do
#story = Story.any_of( { :Tags.in => [/romantic/i, /comedy/i]}....
To create an AND query, it can get a little more complicated. There is an elemMatch function you could use.
I don't think you could do {:Tags => /romantic/i, :Tags => /comedy/i }
So my best thought would be to do sequential queries, even though there would be a performance hit, but if your DB isn't that big, it shouldn't be a big issue. So if you want Romantic AND Comedy you can do
query 1: find all collections that match /romantic/i
query 2: take results of query 1, find all collections that match /comedy/i
And so on by iterating through your array of selectors.
I have a table called items with a type column.
This column can have one of the following values:
rock
paper
scissor
Inside my translation file:
en:
rock: Stone
paper: Wood
scissor: Weapon
How can i fetch the results and order them by the translated value using ActiveRecord?
Obviously, if I do Item.where(something: true).order('name asc') I would get the results ordered by the value inside the database (rock) and not the translated value (Stone).
I am aware of some ruby methods such as sort_by and sort to order items with ruby, but I would like to order the results in ActiveRecord for performance reasons.
I managed to solve this by using a CASE statement.
Item.select("*,
CASE
WHEN type = 'rock' THEN '#{I18n.t(:rock)}'
WHEN type = 'paper' THEN '#{I18n.t(:paper)}'
WHEN type = 'scissor' THEN '#{I18n.t(:scissor)}'
END AS translated_type
")
.where(something: true)
.order('translated_type asc')
This works fine in my case since I know which types to expect.
So, in my rails app I developed a search filter where I am using sliders. For example, I want to show orders where the price is between min value and max value which comes from the slider in params. I have column in my db called "price" and params[:priceMin], params[:priceMax]. So I can't write something kinda MyModel.where(params).... You may say, that I should do something like MyModel.where('price >= ? AND price <= ?', params[:priceMin], params[:priceMax]) but there is a problem: the number of search criteria depends on user desire, so I don't know the size of params hash that passes to query. Are there any ways to solve this problem?
UPDATE
I've already done it this way
def query_senders
query = ""
if params.has_key?(:place_from)
query += query_and(query) + "place_from='#{params[:place_from]}'"
end
if params.has_key?(:expected_price_min) and params.has_key?(:expected_price_max)
query += query_and(query) + "price >= '#{params[:expected_price_min]}' AND price <= '#{params[:expected_price_max]}'"
end...
but according to ruby guides (http://guides.rubyonrails.org/active_record_querying.html) this approach is bad because of SQL injection danger.
You can get the size of params hash by doing params.count. By the way you described it, it still seems that you will know what parameters can be passed by the user. So just check whether they're present, and split the query accordingly.
Edited:
def query_string
return = {}
if params[:whatever].present?
return.merge({whatever: #{params[:whatever]}}"
elsif ...
end
The above would form a hash for all of the exact values you're searching for, avoiding SQL injection. Then for such filters as prices you can just check whether the values are in correct format (numbers only) and only perform if so.