Is there a more idiomatic way to do the following?
var = hash[key].nil? ? nil : hash[key].downcase
Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:
var = hash[key].downcase unless hash[key].nil?
Obviously I could do this, but I'm trying to keep it on one line:
var = hash[key]
var = var.downcase unless var.nil?
--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: ac...@celticwolf.net
Contact Address: accm...@celticwolf.net
I see nothing wrong with an explicit approach. Here is an alternative, though:
var = hash[key].downcase rescue nil
Kirk Haines
I usually do:
hash = { :key1 => nil , :key2 => "aBcD" }
var = hash[:key1] && hash[:key1].downcase
var # => nil
var = hash[:key2] && hash[:key2].downcase
var # => "abcd"
Notice the nuance that if hash[:key1] is false, that will be assigned to
var.
This also works:
var = hash[:key1].downcase rescue nil
var # => nil
var = hash[:key2].downcase rescue nil
var # => "abcd"
But people seem to really be opposed to doing this. Not entirely sure why.
Maybe because it wouldn't raise an error if, for example, you put some
absurd type of data in your hash?
cheat a little?
var = hash[key]; var &&= var.downcase
another idea:
class Object
def and_then
yield self
end
end
class NilClass
def and_then
self
end
end
var = hash[key].and_then {|val| val.downcase}
Absurd? It's an easy to get symbols where strings should be...
key = "a"
hash = {"a" => :Foo}
var = hash[key].downcase rescue nil
p var # ==> nil
On Tue, 12 Oct 2010, Charles Calvert wrote:
> I'm using Ruby 1.8.7 patchlevel 249
>
> Is there a more idiomatic way to do the following?
>
> var = hash[key].nil? ? nil : hash[key].downcase
>
> Note that if hash[key] is nil, I want nil assigned to var, so this
> won't work:
>
> var = hash[key].downcase unless hash[key].nil?
It actually will work if var has not been initialized before. But that's
more or less an artifact of how the parser treats local variable
assignment expressions, and is pretty fragile:
>> hash = {}
=> {}
>> var = hash[1].downcase unless hash[1].nil?
=> nil
>> var
=> nil
>> var2 = "something"
=> "something"
>> var2 = hash[1].downcase unless hash[1].nil?
=> nil
>> var2
=> "something"
David
--
David A. Black, Senior Developer, Cyrus Innovation Inc.
The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com
if hash.has_key?(:key) and hash[:key].is_a?(String)
# do stuff
end
Or, your rescue example... I'd at least rewrite it as:
var = begin
hash[key].downcase
rescue NoMethodError
nil
end
If you use Ruby Ketsup (aka, Active Support), you can put this everywhere:
val = hash[key] if hash[key].present?
#or...
val = hash[key] unless hash[key].blank?
So you know why you're rescuing here. Blanket rescue foolishness can
and will cascade throughout your application. Again, trivial
example... But I think it shouldn't really be done in any
circumstance.
Scott
# Just get a String, rawr
var = begin
hash[key].to_s.downcase
rescue NoMethodError
""
end
# Be a good citizen, and tread carefully
var = case hash[key]
when String then hash[key].downcase
when Numeric then hash[key].to_s
when Array then hash[key].join.downcase # rescue block for safety?
when Hash then hash[key].values.join.downcase # rescue
block for safety?
else puts "LOOK AT WHAT YOU'VE DONE. YOU'VE MADE RUBY CRY."
end
On Tue, 12 Oct 2010, Scott Gonyea wrote:
> Because I love dead horses (or just low productivity). Two more code
> examples:
>
> # Just get a String, rawr
> var = begin
> hash[key].to_s.downcase
> rescue NoMethodError
> ""
> end
You should never get a NoMethodError on #to_s; it's defined on Object.
So you could write that as:
var = hash[key].to_s.downcase
(though in the original post, var was supposed to be set to nil if
hash[key] was nil).
David
--
David A. Black, Senior Developer, Cyrus Innovation Inc.
The Ruby training with Black/Brown/McAnally
Compleat Stay tuned for next event!
Rubyist http://www.compleatrubyist.com
This is identical to saying this:
var = hash[key] && hash[key].to_s.downcase
:: Hope that helps.
Haskell solves the "may be nil" problem with the Maybe monad (from Haskell).
There's many ways to implement something similar... such as implementing
Object#maybe whose method_missing (e.g. Maybe#method_missing) proxies to the
original object. NilClass can also implement #maybe, and always return nil.
On Mon, Oct 11, 2010 at 2:05 PM, Charles Calvert <cb...@yahoo.com> wrote:
--
Tony Arcieri
Medioh! A Kudelski Brand
>[Note: parts of this message were removed to make it a legal post.]
>
>On Mon, Oct 11, 2010 at 3:05 PM, Charles Calvert <cb...@yahoo.com> wrote:
>
>> I'm using Ruby 1.8.7 patchlevel 249
>>
>> Is there a more idiomatic way to do the following?
>>
>> var = hash[key].nil? ? nil : hash[key].downcase
>>
>> Note that if hash[key] is nil, I want nil assigned to var, so this
>> won't work:
>>
>> var = hash[key].downcase unless hash[key].nil?
>>
>> Obviously I could do this, but I'm trying to keep it on one line:
>>
>> var = hash[key]
>> var = var.downcase unless var.nil?
>
>I usually do:
>hash = { :key1 => nil , :key2 => "aBcD" }
>
>var = hash[:key1] && hash[:key1].downcase
>var # => nil
>
>var = hash[:key2] && hash[:key2].downcase
>var # => "abcd"
Using the short-circuit evaluation of && to avoid calling downcase.
Interesting.
>Notice the nuance that if hash[:key1] is false, that will be assigned to
>var.
Yes, that's a weakness of the technique, though it wouldn't matter in
the use case at hand.
>This also works:
>
>var = hash[:key1].downcase rescue nil
>var # => nil
>
>var = hash[:key2].downcase rescue nil
>var # => "abcd"
>
>But people seem to really be opposed to doing this. Not entirely sure why.
>Maybe because it wouldn't raise an error if, for example, you put some
>absurd type of data in your hash?
I don't have enough experience with exception handling in Ruby to know
for sure, but I suspect that it's considered a bad practice because it
might accidentally catch exceptions other than the intended one. It's
being discussed in another subthread, so I'll watch that.
>On 10/11/2010 01:05 PM, Charles Calvert wrote:
>> I'm using Ruby 1.8.7 patchlevel 249
>>
>> Is there a more idiomatic way to do the following?
>>
>> var = hash[key].nil? ? nil : hash[key].downcase
>>
>> Note that if hash[key] is nil, I want nil assigned to var, so this
>> won't work:
>>
>> var = hash[key].downcase unless hash[key].nil?
>>
>> Obviously I could do this, but I'm trying to keep it on one line:
>>
>> var = hash[key]
>> var = var.downcase unless var.nil?
>
>cheat a little?
>
>var = hash[key]; var &&= var.downcase
Well, yes, I suppose that I could do that. :) I think that the
trinary operator is a bit more obvious, but then I'm used to it.
>another idea:
>
>class Object
> def and_then
> yield self
> end
>end
>
>class NilClass
> def and_then
> self
> end
>end
>
>var = hash[key].and_then {|val| val.downcase}
Clever, but requires patching core classes, which I'd like to avoid.
>On Tue, 12 Oct 2010, Charles Calvert wrote:
>
>> I'm using Ruby 1.8.7 patchlevel 249
>>
>> Is there a more idiomatic way to do the following?
>>
>> var = hash[key].nil? ? nil : hash[key].downcase
>>
>> Note that if hash[key] is nil, I want nil assigned to var, so this
>> won't work:
>>
>> var = hash[key].downcase unless hash[key].nil?
>
>It actually will work if var has not been initialized before. But that's
>more or less an artifact of how the parser treats local variable
>assignment expressions, and is pretty fragile:
Exactly. Plus, it's programming by side effect, which gives me the
willies. I wondered if someone would bring it up, though. :)
[snip proof]
In my use case, true, because the type of the value will either be
NilClass or String. As Josh Cheek pointed out, though, if hash[key]
returns false, var would be false, rather than "false".
Thanks for the suggestion.
>
>var = hash[key].downcase if hash[key]
This is a variant of:
var = hash[key].downcase unless hash[key].nil?
which I indicated in my original post won't work, as I want an
explicit assignment of nil to var in the event that hash[key] returns
nil. See David Black's response to my post. I'm not assuming that
this is the first occurrence of var, so it may already have a value
from a previous assignment.
You can get around this by putting ( ) around the whole if clause:
var = (hash[key].downcase if hash[key])
It works because (... if ...) returns nil if the condition is false.
Nobody seems to have mentioned it, I guess since this is the place for
'pure' rubyists, but Rails has the following methods:
class Object
def try(method, *args, &block)
send(method, *args, &block)
end
end
class NilClass
def try(*args)
nil
end
end
So with this you can write simply
var = hash[key].try(:downcase)
--
Rick DeNatale
Help fund my talk at Ruby Conf 2010:http://pledgie.com/campaigns/13677
Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
On Tue, Oct 12, 2010 at 6:39 AM, Rick DeNatale <rick.d...@gmail.com>wrote:
> Nobody seems to have mentioned it, I guess since this is the place for
> 'pure' rubyists, but Rails has the following methods:
>
> So with this you can write simply
>
> var = hash[key].try(:downcase)
>
That's pretty cool, but I'd prefer it return a proxy object for all non-nil
objects that just relays the method call, and for nil a proxy that always
returns nil, ala:
hash[key].try.downcase
Like this?
require 'singleton'
class NilProxy
include Singleton
def method_missing(*a,&b) end
end
class Object
def try
self
end
end
class NilClass
def try
NilProxy.instance
end
end
["Foo", nil, "Bar"].each do |x|
p x.try.downcase
end
Kind regards
robert
--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
While that would clearly work, it makes me a little nervous as it
isn't immediately obvious, to me at least. Most likely I just haven't
spent enough time with Ruby. :)
I agree. It reduces readability, and it's easy to forget the parens.
On Wed, Oct 13, 2010 at 3:18 AM, Robert Klemme
<short...@googlemail.com>wrote:
> Like this?
>
Yes :)
On Mon, Oct 11, 2010 at 10:04 PM, Joel VanderWerf
<joelvan...@gmail.com>wrote:
> You can get around this by putting ( ) around the whole if clause:
>
> var = (hash[key].downcase if hash[key])
>
> It works because (... if ...) returns nil if the condition is false.
>
The parens are unnecessary. The "var" variable will be implicitly bound to
nil if the if condition isn't true.
As David Black pointed out:
var = 1
var = 2 if false
p var # ==> 1
On Wed, Oct 13, 2010 at 6:13 PM, Joel VanderWerf
<joelvan...@gmail.com>wrote:
> As David Black pointed out:
>
> var = 1
> var = 2 if false
> p var # ==> 1
>
Well, avoiding destructive assignments is an alternative to wrapping the
whole expression in parens
I'm not sure if this is the most common idiom, but you can write a
helper method like "try" that chains method calls but aborts nicely to
nil as soon as an object dereference (or the primary object itself)
evaluates to nil.
The example below is slightly fancier than you might need, as it
supports multiple dereferences. Scroll down to the bottom to see its
usage.
$ cat foo.rb
def try(obj, *args)
for arg in args
return nil if obj.nil?
obj = obj.send(arg)
end
return obj
end
class Mission
end
class Department
def name
'HR'
end
def mission
return nil
end
end
class Employee
def department
return Department.new()
end
def name
# oops, no name field
return nil
end
end
emp = nil
puts try(emp, :mission, :department, :name)
emp = Employee.new
puts try(emp, :department, :name)
$ ruby foo.rb
nil
HR
--
Posted via http://www.ruby-forum.com/.
>On Oct 11, 1:02�pm, Charles Calvert <cb...@yahoo.com> wrote:
>> I'm using Ruby 1.8.7 patchlevel 249
>>
>> Is there a more idiomatic way to do the following?
>>
>> var = hash[key].nil? ? nil : hash[key].downcase
>>
>> Note that if hash[key] is nil, I want nil assigned to var, so this
>> won't work:
>>
>> var = hash[key].downcase unless hash[key].nil?
>>
>> Obviously I could do this, but I'm trying to keep it on one line:
>>
>> var = hash[key]
>> var = var.downcase unless var.nil?
>>
>
>I'm not sure if this is the most common idiom, but you can write a
>helper method like "try" that chains method calls but aborts nicely to
>nil as soon as an object dereference (or the primary object itself)
>evaluates to nil.
[snip example]
I was looking for something that didn't require patching existing
classes, but it's becoming clearer to me that such patches are much
more common than I thought and can yield cleaner code. Thanks for the
example.
var = (t = h[key] and t.upcase) or nil
var = h[key].tap{|x| x ? x.upcase : nil}
Using short circuit evaluation to avoid the call to upcase if h[key]
is nil, check.
>var = h[key].tap{|x| x ? x.upcase : nil}
Unfortunately tap is Ruby 1.9, and as I said above, I'm using 1.8.7.
Thanks for the answers.
h[key] will return nil if key is not found, so you do not need that "or nil"
var = (t = h[key] and t.upcase)
> var = h[key].tap{|x| x ? x.upcase : nil}
That is not gonna work, Object#tap return receiver.
So the only way to modify the object "tapped" is to call mutating methods on it:
var = h[key].tap { |x| x ? x.upcase! : nil }
s = hash[:foo] and s.downcase!