if args.size == 5
value_for,alt_currency_id,amount,exchange_rate_code,tran_dt = args
else
value_for,alt_currency_id,amount,exchange_rate_code,year_no,period_no = args
end
Any Better way to write this condition ??
I would just skip the condition entirely. If you don't have the fifth argument, period_no will simply be nil.
If period_no needed to be set to some default you could follow up with:
period_no ||= sane_default
Definitely is a code smell, specially since the variable is called args. If you're passing all these arguments as optional values, the best approach is make the variable arguments into a hash.
def whatever(value_for, alt_currency_id, amount, options = {})
tran_dt = options[:tran_dt]
year_no = options[:year_no]
period_no = options[:period_no]
...
end
To strictly meet your requirements, I'd do this:
value_for, alt_currency_id, amount, exchange_rate_code = args.shift(4)
tran_dt, year_no, period_no = [nil, nil, nil] # or some sensible defaults
case args.size
when 1 then tran_dt = args.shift
when 2 then year_no, period_no = args.shift(2)
end
But this code has a smell to it. I'd look at redesigning how that method gets called.
Perhaps assign period_no to nil by default, and use that to determine which argument set you are working with:
def process_record(value_for, alt_currency_id, amount, exchange_rate_code, tran_dt, period_no=nil)
year_no = period_no ? tran_dt : nil
puts "tran_dt: #{tran_dt.inspect}"
puts "year_no: #{year_no.inspect}"
puts "period_no: #{period_no.inspect}"
end
process_record(:foo, :bar, :baz, :buz, Time.now)
# Output:
#
# tran_dt: Mon Sep 13 15:52:54 -0400 2010
# year_no: nil
# period_no: nil
process_record(:foo, :bar, :baz, :buz, 2010, 1)
# Output:
#
# tran_dt: 2010
# year_no: 2010
# period_no: 1
Here's one way of DRYing up your code a bit:
value_for, alt_currency_id, amount, exchange_rate_code, year_no, period_no = args
if period_no.nil?
tran_dt = year_no
year_no = nil # May or may not be needed, depending on later code
end
Ruby has two ternary operators as well that I'm aware of
a = true ? 'a' : 'b' #=> "a"
b = false ? 'a' : 'b' #=> "b"
or
a = (true && 'a') || b #=> "a"
b = (false && 'a') || b #=> "b"
Are you processing commandline? Just leave as it is, for me it is most readable at first look :) Otherwise it may smell perlish.
You simply see what is required set for 5 arguments or else.
If these are not command line args I suggest introducing hash.
Related
What does the following code mean in Ruby?
||=
Does it have any meaning or reason for the syntax?
a ||= b is a conditional assignment operator. It means:
if a is undefined or falsey, then evaluate b and set a to the result.
Otherwise (if a is defined and evaluates to truthy), then b is not evaluated, and no assignment takes place.
For example:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Confusingly, it looks similar to other assignment operators (such as +=), but behaves differently.
a += b translates to a = a + b
a ||= b roughly translates to a || a = b
It is a near-shorthand for a || a = b. The difference is that, when a is undefined, a || a = b would raise NameError, whereas a ||= b sets a to b. This distinction is unimportant if a and b are both local variables, but is significant if either is a getter/setter method of a class.
Further reading:
http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
This question has been discussed so often on the Ruby mailing-lists and Ruby blogs that there are now even threads on the Ruby mailing-list whose only purpose is to collect links to all the other threads on the Ruby mailing-list that discuss this issue.
Here's one: The definitive list of ||= (OR Equal) threads and pages
If you really want to know what is going on, take a look at Section 11.4.2.3 "Abbreviated assignments" of the Ruby Language Draft Specification.
As a first approximation,
a ||= b
is equivalent to
a || a = b
and not equivalent to
a = a || b
However, that is only a first approximation, especially if a is undefined. The semantics also differ depending on whether it is a simple variable assignment, a method assignment or an indexing assignment:
a ||= b
a.c ||= b
a[c] ||= b
are all treated differently.
Concise and complete answer
a ||= b
evaluates the same way as each of the following lines
a || a = b
a ? a : a = b
if a then a else a = b end
-
On the other hand,
a = a || b
evaluates the same way as each of the following lines
a = a ? a : b
if a then a = a else a = b end
-
Edit: As AJedi32 pointed out in the comments, this only holds true if: 1. a is a defined variable. 2. Evaluating a one time and two times does not result in a difference in program or system state.
In short, a||=b means: If a is undefined, nil or false, assign b to a. Otherwise, keep a intact.
Basically,
x ||= y means
if x has any value leave it alone and do not change the value, otherwise
set x to y
It means or-equals to. It checks to see if the value on the left is defined, then use that. If it's not, use the value on the right. You can use it in Rails to cache instance variables in models.
A quick Rails-based example, where we create a function to fetch the currently logged in user:
class User > ActiveRecord::Base
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
end
It checks to see if the #current_user instance variable is set. If it is, it will return it, thereby saving a database call. If it's not set however, we make the call and then set the #current_user variable to that. It's a really simple caching technique but is great for when you're fetching the same instance variable across the application multiple times.
To be precise, a ||= b means "if a is undefined or falsy (false or nil), set a to b and evaluate to (i.e. return) b, otherwise evaluate to a".
Others often try to illustrate this by saying that a ||= b is equivalent to a || a = b or a = a || b. These equivalencies can be helpful for understanding the concept, but be aware that they are not accurate under all conditions. Allow me to explain:
a ||= b ⇔ a || a = b?
The behavior of these statements differs when a is an undefined local variable. In that case, a ||= b will set a to b (and evaluate to b), whereas a || a = b will raise NameError: undefined local variable or method 'a' for main:Object.
a ||= b ⇔ a = a || b?
The equivalency of these statements are often assumed, since a similar equivalence is true for other abbreviated assignment operators (i.e. +=,-=,*=,/=,%=,**=,&=,|=,^=,<<=, and >>=). However, for ||= the behavior of these statements may differ when a= is a method on an object and a is truthy. In that case, a ||= b will do nothing (other than evaluate to a), whereas a = a || b will call a=(a) on a's receiver. As others have pointed out, this can make a difference when calling a=a has side effects, such as adding keys to a hash.
a ||= b ⇔ a = b unless a??
The behavior of these statements differs only in what they evaluate to when a is truthy. In that case, a = b unless a will evaluate to nil (though a will still not be set, as expected), whereas a ||= b will evaluate to a.
a ||= b ⇔ defined?(a) ? (a || a = b) : (a = b)????
Still no. These statements can differ when a method_missing method exists which returns a truthy value for a. In this case, a ||= b will evaluate to whatever method_missing returns, and not attempt to set a, whereas defined?(a) ? (a || a = b) : (a = b) will set a to b and evaluate to b.
Okay, okay, so what is a ||= b equivalent to? Is there a way to express this in Ruby?
Well, assuming that I'm not overlooking anything, I believe a ||= b is functionally equivalent to... (drumroll)
begin
a = nil if false
a || a = b
end
Hold on! Isn't that just the first example with a noop before it? Well, not quite. Remember how I said before that a ||= b is only not equivalent to a || a = b when a is an undefined local variable? Well, a = nil if false ensures that a is never undefined, even though that line is never executed. Local variables in Ruby are lexically scoped.
If X does NOT have a value, it will be assigned the value of Y. Else, it will preserve it's original value, 5 in this example:
irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5
# Now set x to nil.
irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10
x ||= y
is
x || x = y
"if x is false or undefined, then x point to y"
||= is a conditional assignment operator
x ||= y
is equivalent to
x = x || y
or alternatively
if defined?(x) and x
x = x
else
x = y
end
unless x
x = y
end
unless x has a value (it's not nil or false), set it equal to y
is equivalent to
x ||= y
Suppose a = 2 and b = 3
THEN, a ||= b will be resulted to a's value i.e. 2.
As when a evaluates to some value not resulted to false or nil.. That's why it ll not evaluate b's value.
Now Suppose a = nil and b = 3.
Then a ||= b will be resulted to 3 i.e. b's value.
As it first try to evaluates a's value which resulted to nil.. so it evaluated b's value.
The best example used in ror app is :
#To get currently logged in iser
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
Where, User.find_by_id(session[:user_id]) is fired if and only if #current_user is not initialized before.
||= is called a conditional assignment operator.
It basically works as = but with the exception that if a variable has already been assigned it will do nothing.
First example:
x ||= 10
Second example:
x = 20
x ||= 10
In the first example x is now equal to 10. However, in the second example x is already defined as 20. So the conditional operator has no effect. x is still 20 after running x ||= 10.
a ||= b
Signifies if any value is present in 'a' and you dont want to alter it the keep using that value, else if 'a' doesnt have any value, use value of 'b'.
Simple words, if left hand side if not null, point to existing value, else point to value at right side.
a ||= b
is equivalent to
a || a = b
and not
a = a || b
because of the situation where you define a hash with a default (the hash will return the default for any undefined keys)
a = Hash.new(true) #Which is: {}
if you use:
a[10] ||= 10 #same as a[10] || a[10] = 10
a is still:
{}
but when you write it like so:
a[10] = a[10] || 10
a becomes:
{10 => true}
because you've assigned the value of itself at key 10, which defaults to true, so now the hash is defined for the key 10, rather than never performing the assignment in the first place.
It's like lazy instantiation.
If the variable is already defined it will take that value instead of creating the value again.
Please also remember that ||= isn't an atomic operation and so, it isn't thread safe. As rule of thumb, don't use it for class methods.
This is the default assignment notation
for example: x ||= 1
this will check to see if x is nil or not. If x is indeed nil it will then assign it that new value (1 in our example)
more explicit:
if x == nil
x = 1
end
b = 5
a ||= b
This translates to:
a = a || b
which will be
a = nil || 5
so finally
a = 5
Now if you call this again:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Now if you call this again:
a ||= b
a = a || b
a = 5 || 6
a = 5
If you observe, b value will not be assigned to a. a will still have 5.
Its a Memoization Pattern that is being used in Ruby to speed up accessors.
def users
#users ||= User.all
end
This basically translates to:
#users = #users || User.all
So you will make a call to database for the first time you call this method.
Future calls to this method will just return the value of #users instance variable.
As a common misconception, a ||= b is not equivalent to a = a || b, but it behaves like a || a = b.
But here comes a tricky case. If a is not defined, a || a = 42 raises NameError, while a ||= 42 returns 42. So, they don't seem to be equivalent expressions.
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
Because a was already set to 1
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
Because a was nil
This ruby-lang syntax. The correct answer is to check the ruby-lang documentation. All other explanations obfuscate.
Google
"ruby-lang docs Abbreviated Assignment".
Ruby-lang docs
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
a ||= b is the same as saying a = b if a.nil? or a = b unless a
But do all 3 options show the same performance? With Ruby 2.5.1 this
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
takes 0.099 Seconds on my PC, while
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
takes 0.062 Seconds. That's almost 40% faster.
and then we also have:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
which takes 0.166 Seconds.
Not that this will make a significant performance impact in general, but if you do need that last bit of optimization, then consider this result.
By the way: a = 1 unless a is easier to read for the novice, it is self-explanatory.
Note 1: reason for repeating the assignment line multiple times is to reduce the overhead of the loop on the time measured.
Note 2: The results are similar if I do a=nil nil before each assignment.
I would like to compare two hashes and forces them to be equal:
one with Symbols on keys and values
the second with only strings.
e.g:
sym_hash = {:id=>58, :locale=>:"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
Try like this does not work:
> sym_hash == string_hash
=> false
I first tried to symbolized the string_hash:
> string_hash.deep_symbolize_keys
=> {:id=>58, :locale=>"en-US"}
But it is still false because sym_hash still has : in front of locale var.
Then I tried to stringified the sym_hash:
> sym_hash.with_indifferent_access
=> {"id"=>58, "locale"=>:"en-US"}
But when I test for equality it is still false for the same reasons.
EDIT
To answer many comments abouy why I wanted those hashes to be equal here, I'll explain what I'm trying to do.
I'm using Reque to manage my jobs. Now I wanted to do a class to avoid having the same* job running, or being enqueued twice in the same time.
(same: for me the same job is a job having the same parameters, I would like to be able to enqueu twice the same jobs having differents ids for instance.)
For that I'm a using the plugin resque-status, so far I'm able to know when a job is running or not. Beside, when I save the params using set I notice that the message written to Redis(because resque-status is using Redis to keep track of the job's status) is not properly saved with symbols.
Here is my class:
# This class is used to run thread-lock jobs with Resque.
#
# It will check if the job with the exact same params is already running or in the queue.
# If the job is not finished, it will returns false,
# otherwise, it will run and returns a the uuid of the job.
#
class JobLock
def self.run(obj, params = {})
# Get rid of completed jobs.
Resque::Plugins::Status::Hash.clear_completed
# Check if your job is currently running or is in the queue.
if !detect_processing_job(obj, params)
job_uuid = obj.create(params)
Resque::Plugins::Status::Hash.set(job_uuid,
job_name: obj.to_s,
params: params)
job_uuid
else
false
end
end
def self.detect_processing_job(obj, params = {})
Resque::Plugins::Status::Hash.statuses.detect do |job|
job['job_name'] == obj.to_s && compare_hashes(job['params'], params)
end
end
def self.compare_hashes(string_hash, sym_hash)
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map(&:to_s) }.sort
end.reduce :==
end
end
And here how I can use it:
JobLock.run(MyAwesomeJob, id: 58, locale: :"en-US")
As you can see I used #mudasobwa's answer but I hope there is a easier way to achieve what I am trying to do!
How about this?
require 'set'
def sorta_equal?(sym_hash, str_hash)
return false unless sym_hash.size == str_hash.size
sym_hash.to_a.to_set == str_hash.map { |pair|
pair.map { |o| o.is_a?(String) ? o.to_sym : o } }.to_set
end
sym_hash= {:id=>58, :locale=>:"en-US"}
sorta_equal?(sym_hash, {"id"=>58, "locale"=>"en-US"}) #=> true
sorta_equal?(sym_hash, {"locale"=>"en-US", "id"=>58 }) #=> true
sorta_equal?(sym_hash, {"id"=>58, "local"=>"en-US", "a"=>"b" }) #=> false
sorta_equal?(sym_hash, {"id"=>58, "lacole"=>"en-US"}) #=> false
sorta_equal?(sym_hash, {"id"=>58, [1,2,3]=>"en-US"}) #=> false
sorta_equal?({}, {}) #=> true
class A; end
a = A.new
sorta_equal?({:id=>a, :local=>:b}, {"id"=>a, "local"=>"b"}) #=> true
You could try to convert both hashes to JSON, and then compare them:
require 'json'
# => true
sym_hash = {:id=>58, :locale=>:"en-US"}
# => {:id=>58, :locale=>:"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
# => {"id"=>58, "locale"=>"en-US"}
sym_hash.to_json == string_hash.to_json
# => true
The version below works as PHP force-coercion equality:
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map(&:to_s) }.sort
end.reduce :==
BTW, it’s not a one-liner only because I respect people with smartphones. On terminals of width 80 it’s a perfect oneliner.
To coerce only symbols to strings, preserving numerics to be distinguished from their string representations:
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map { |e| e.is_a?(Symbol) ? e.to_s : e } }.sort
end.reduce :==
The value of locale in sym_hash is a Symbol :"en-US",
while the value of locale in string_hash is a String.
So they are not equal.
Now if you do:
sym_hash = {:id=>58, :locale=>"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
string_hash.symbolize_keys!
sym_hash == string_hash
=> true
Finaly, to answer my problem I didn't need to force comparaison between hashes. I use Marshal to avoid the problem
class JobLock
def self.run(obj, params = {})
# Get rid of completed jobs.
Resque::Plugins::Status::Hash.clear_completed
# Check if your job is currently running or is in the queue.
if !detect_processing_job(obj, params)
job_uuid = obj.create(params)
Resque::Plugins::Status::Hash.set(job_uuid,
job_name: obj.to_s,
params: Marshal.dump(params))
job_uuid
else
false
end
end
def self.detect_processing_job(obj, params = {})
Resque::Plugins::Status::Hash.statuses.detect do |job|
job['job_name'] == obj.to_s && Marshal.load(job['params']) == params
end
end
end
Anyway, I let this question here because maybe it will help some people in the future...
This is a slight deviation from the original question and an adaptation of some of the suggestions above. If the values can also be String / Symbol agnostic, then may I suggest:
def flat_hash_to_sorted_string_hash(hash)
hash.map { |key_value| key_value.map(&:to_s) }.sort.to_h.to_json
end
this helper function can then be used to assert two hashes have effectively the same values without being type sensitive
assert_equal flat_hash_to_sorted_string_hash({ 'b' => 2, a: '1' }), flat_hash_to_sorted_string_hash({ b: '2', 'a' => 1 }) #=> true
Breakdown:
by mapping a hash, the result is an array
by making the keys / values a consistent type, we can leverage the Array#sort method without raising an error: ArgumentError: comparison of Array with Array failed
sorting gets the keys in a common order
to_h return the object back to a hash
NOTE: this will not work for complex hashes with nested objects or for Float / Int, but as you can see the Int / String comparison works as well. This was inspired by the JSON approach already discussed, but without needing to use JSON, and felt like more than a comment was warranted here as this was the post I found the inspiration for the solution I was seeking.
I've a method like:
def method (a=[],b=[])
...
end
I am calling this method with variables like this:
method(h[0].values_at("value"), h[1].values_at("value"))
where h[0] and h[1] are hashes. It works fine.
I dont know if h[1] is going to be there in the next run, so it's giving me error if hash h[1] is not there.
How can I modify it so it makes the call dynamically depending on whether h[0], h[1] are there or not, and call the method with the correct number of parameters.
Hope I understood your problem right:
method(h[0].values_at("value"),
h[1] ? h[1].values_at("value") : []
)
Your problem: if h[1]does not exist, h[1].values_at will raise an exception. So you have to test first, if the value is available. In the code snipllet above I used a ternary operator.
An extended version would be:
par2 = []
par2 = h[1].values_at("value") if h[1]
method(h[0].values_at("value"),
par2
)
With my solution you don't need the default values in the method definition.
In your comment you extended your question.
With four parameters you could use it like this:
def method(p1,p2,p3,p4)
#...
end
method(
h[0] ? h[0].values_at("value") : [],
h[1] ? h[1].values_at("value") : [],
h[2] ? h[2].values_at("value") : [],
h[3] ? h[3].values_at("value") : [],
)
But I would recommend a more generic version:
def method(*pars)
#pars is an Array with all values (including empty arrays.
#The next check make only sense, if you need exactly 4 values.
raise "Expected 4 values, get #{pars.size}" unless pars.size == 4
end
method( *(h.map{|i|i.values_at("x")})
And another - probably not so good - idea:
Extend nil (the result of h[1] if h has not this element) to return [] for values_at:
class NilClass
def values_at(x)
[]
end
end
The simplest way to do exactly what you ask would be:
if h[1]
method(h[0].values_at("x"), h[1].values_at("x"))
else
method(h[0].values_at("x"))
end
Another idea is to put a default hash in the case where h[1] is nil
method(h[0].values_at("x"), (h[1]||{}).values_at("x") )
If you are sure h never has more than 2 items, you can do:
method *(h.map{|i|i.values_at("x")})
If you use Ruby On Rails you are able to execute try method:
method(h[0].values_at("value"), h[1].try(:values_at, 'value') || [])
Examples for method try:
nil.try('[]', :x) # => nil
{:x => 't'}.try('[]', :x) # => 't'
You can change the function signature to accept variable arguments.
def foo(*args)
options = args.extract_options!
p options
p args
end
Invocation without parameters
foo()
{}
[]
Invocation with 2 parameters
foo(1, 2)
{}
[1, 2]
Invocation with 3 parameters
foo(1, 2, 3)
{}
[1, 2, 3]
Invocation with 3 parameters and an option hash
foo(1, 2, 3, :debug => true)
{:debug=>true}
[1, 2, 3]
Is there a short hand or best practice for assigning things to a hash when they are nil in ruby? For example, my problem is that I am using another hash to build this and if something in it is nil, it assigns nil to that key, rather than just leaving it alone. I understand why this happens so my solution was:
hash1[:key] = hash2[:key] unless hash2[:key].nil?
Because I cannot have a value in the has where the key actually points to nil. (I would rather have an empty hash than one that has {:key => nil}, that can't happen)
My question would be is there a better way to do this? I don't want to do a delete_if at the end of the assignments.
a little bit shorter if you negate the "unless" statement
hash1[:key] = hash2[:key] if hash2[:key] # same as if ! hash2[:key].nil?
you could also do the comparison in a && statement as suggested in other answers by Michael or Marc-Andre
It's really up to you, what you feel is most readable for you. By design, there are always multiple ways in Ruby to solve a problem.
You could also modify the hash2 :
hash1 = hash2.reject{|k,v| v.nil?}
hash2.reject!{|k,v| v.nil?} # even shorter, if in-place editing of hash2
this would remove key/value pairs :key => nil from hash2 (in place, if you use reject! )
I like this the best, loop and conditional overriding all in one line!
h1 = {:foo => 'foo', :bar => 'bar'}
h2 = {:foo => 'oof', :bar => nil}
h1.merge!(h2) { |key, old_val, new_val| new_val.nil? ? old_val : new_val }
#=> {:foo => 'oof', :bar => 'bar'}
This will replace every value in h1 with the value of h2 where the keys are the same and the h2 value is not nil.
I'm not sure if that's really any better, but
hash2[:key] && hash[:key] = hash2[:key]
could work. Note that this would behave the same way for false and nil, if that's not what you want
!hash2[:key].nil? && hash[:key] = hash2[:key]
would be better. All of this assuming that :key would be an arbitrary value that you may not have control over.
How about something like this?
hash2.each_pair do |key, value|
next if value.nil?
hash1[key] = value
end
If you are doing just a single assignment, this could shave a few characters:
hash2[:key] && hash1[:key] = hash2[:key]
My first example could also be shaved a bit further:
hash2.each_pair{ |k,v| v && hash1[k] = v }
I think the first is the easiest to read/understand. Also, examples 2 and 3 will skip anything that evaluates false (nil or false). This final example is one line and won't skip false values:
hash2.each_pair{ |k,v| v.nil? || hash1[k] = v }
I believe the best practice is to copy the nil value over to the hash. If one passes an option :foo => nil, it can mean something and should override a default :foo of 42, for example. This also makes it easier to have options which should default to true, although one should use fetch in those cases:
opt = hash.fetch(:do_cool_treatment, true) # => will be true if key is not present
There are many ways to copy over values, including nil or false.
For a single key, you can use has_key? instead of the lookup:
hash1[:key] = hash2[:key] if hash2.has_key? :key
For all (or many) keys, use merge!:
hash1.merge!(hash2)
If you only want to do this for a couple of keys of hash2, you can slice it:
hash1.merge!(hash2.slice(:key, ...))
OK, so if the merge doesn't work because you want more control:
hash1[:key] = hash2.fetch(:key, hash1[:key])
This will set hash1's :key to be hash2, unless it doesn't exist. In that case, it will use the default value (2nd argument to fetch), which is hash1's key
Add this to your initializers hash.rb
class Hash
def set_safe(key,val)
if val && key
self[key] = val
end
end
end
use
hash = {}
hash.set_safe 'key', value_or_nil
I have a date_select in my view inside a form, however on submit the value returned is in a hash form like so:
{"(1i)"=>"2010", "(2i)"=>"8", "(3i)"=>"16"}
how can i convert that in to a Date format in rails so i can use it as a condition when querying the database e.g :condition => {:dates == :some_date_from_date_select}? i tried calling Date.parse(:some_date_from_date_select) but it didn't work because its expecting a string and not a hash map.
is there a rails way to do this?
thanks
I don't know about a rails way, but this "one-liner" does the trick:
irb(main):036:0> d = Date.parse( {"(1i)"=>"2010", "(2i)"=>"8", "(3i)"=>"16"}.to_a.sort.collect{|c| c[1]}.join("-") )
=> #<Date: 4910849/2,0,2299161>
irb(main):037:0> d.to_s
=> "2010-08-16"
Or, with less magic:
h={"(1i)"=>"2010", "(2i)"=>"8", "(3i)"=>"16"}
d=Date.new(h['(1i)'].to_i, h['(2i)'].to_i, h['(3i)'].to_i)
d.to_s
=> "2010-08-16"
I have a short one line solution for this
params["due_date"] = {"date(3i)"=>"14", "date(2i)"=>"4", "date(1i)"=>"2014"}
params["due_date"].map{|k,v| v}.join("-").to_date
=> Mon, 14 Apr 2014
Here's a generic way to do it, which also supports partial dates/times and empty fields:
def date_from_date_select_fields(params, name)
parts = (1..6).map do |e|
params["#{name}(#{e}i)"].to_i
end
# remove trailing zeros
parts = parts.slice(0, parts.rindex{|e| e != 0}.to_i + 1)
return nil if parts[0] == 0 # empty date fields set
Date.new(*parts)
end
Example usage:
# example input:
#
# params = {
# "user":
# "date_of_birth(1i)": "2010",
# "date_of_birth(2i)": "8",
# "date_of_birth(3i)": "16"
# }
# }
date_of_birth = date_from_date_select_fields(params[:user], 'date_of_birth')
Date.new(*params["due_date"].values.map(&:to_i))
Note: Works in ruby 1.9.2+ since it depends on the order of the hash elements.
Two goodies here:
Symbol to Proc
Splat operator
This particular code (the one that does conversion) can be tracked from lib/active_record/connection_adapters/abstract/schema_definitions.rb, line no 67 onwards, i.e. the method type_cast.
These two methods are used to generate a date from string:
def fast_string_to_date(string)
if string =~ Format::ISO_DATE
new_date $1.to_i, $2.to_i, $3.to_i
end
end
# Doesn't handle time zones.
def fast_string_to_time(string)
if string =~ Format::ISO_DATETIME
microsec = ($7.to_f * 1_000_000).to_i
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
end
end
# Note that ISO_DATE is:
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/