Conditional keys in hash - out of the box?

Hi

Cumbersome title, here's what I'd like to do both elegantly and without
monkey patching Hash:

Say, I need to pass a hash to a method:

mymethod :this => 'green'

Now I'd like to include a second hash member only if a condition is met:

mymethod :this => 'green, :that => ('blue' if condition)

This of course leaves ":that => nil" in the hash if the condition is not
met - which is no good, the key should just not be present at all. I
could delete_if after that, but isn't there a more elegant way?

Thanks for your ideas!

···

--
Posted via http://www.ruby-forum.com/.

I'm not sure I understand. You want too add :that => 'blue' to the
hash only if condition?

h = {:this => 'green'}
h[:that] = 'blue' if condition
mymethod h

One liner:

mymethod(h = {:this => 'green'} && condition ? h.merge({:that => 'blue'}) : h)

A bit ugly, in my opinion. I like my first way better.

Jesus.

···

On Fri, Mar 5, 2010 at 8:25 PM, Sven S. <svoop@delirium.ch> wrote:

Hi

Cumbersome title, here's what I'd like to do both elegantly and without
monkey patching Hash:

Say, I need to pass a hash to a method:

mymethod :this => 'green'

Now I'd like to include a second hash member only if a condition is met:

mymethod :this => 'green, :that => ('blue' if condition)

This of course leaves ":that => nil" in the hash if the condition is not
met - which is no good, the key should just not be present at all. I
could delete_if after that, but isn't there a more elegant way?

Hi

Cumbersome title, here's what I'd like to do both elegantly and without
monkey patching Hash:

Say, I need to pass a hash to a method:

mymethod :this => 'green'

Now I'd like to include a second hash member only if a condition is met:

mymethod :this => 'green, :that => ('blue' if condition)

This of course leaves ":that => nil" in the hash if the condition is not
met - which is no good, the key should just not be present at all. I
could delete_if after that, but isn't there a more elegant way?

p({this: 'green'}.merge(1 == 2 ? {that: 'blue'} : {}))
{:this=>"green"}

p({this: 'green'}.merge(1 == 1 ? {that: 'blue'} : {}))
{:this=>"green", :that=>"blue"}

···

On Sat, Mar 6, 2010 at 4:25 AM, Sven S. <svoop@delirium.ch> wrote:

Thanks for your ideas!
--
Posted via http://www.ruby-forum.com/\.

--
Michael Fellinger
CTO, The Rubyists, LLC

Sven S. wrote:

Hi

Cumbersome title, here's what I'd like to do both elegantly and without
monkey patching Hash:

Say, I need to pass a hash to a method:

mymethod :this => 'green'

Now I'd like to include a second hash member only if a condition is met:

mymethod :this => 'green, :that => ('blue' if condition)

This of course leaves ":that => nil" in the hash if the condition is not
met - which is no good, the key should just not be present at all. I
could delete_if after that, but isn't there a more elegant way?

Thanks for your ideas!

Here are two suggestions that are more verbose, but also more readable, especially if you have many arguments or more complex logic:

   def with_params
     h = {}
     yield h
     h
   end

   def mymethod h
     p h
   end

   a = 1
   mymethod with_params {|h|
     h[:this] = 'green'
     h[:that] = 'blue' if a==2
   }

You can make this a little tighter, at the expense of changing scope within the block:

   class ParamHash < Hash
     def method_missing(k, v, *)
       self[k] = v
     end
   end

   def with_params2(&block)
     ph = ParamHash.new
     ph.instance_eval(&block)
     ph
   end

   a = 1
   mymethod with_params2 {
     this 'green'
     that 'blue' if a==2
   }

Warning: because of the instance_eval, self is not useful in this block. For example,

     something 'red' if @x==1

or

     something 'red' if foo()==1

won't work as expected. Also, in this second example, unlike the first, your keys will be limited to symbols which are not instance methods of Object or Kernel (or you could use a blank slate).

Jesús Gabriel y Galán wrote:

One liner:

mymethod(h = {:this => 'green'} && condition ? h.merge({:that =>
'blue'}) : h)

You could also do it this way:

method {:Apples => 1}.merge(condition ? {:Oranges => 2} : {})

···

--
Posted via http://www.ruby-forum.com/\.

Oh, good one ! That's much clearer than my one liner. I still prefer
the expanded solution though.

Jesus.

···

On Fri, Mar 5, 2010 at 9:07 PM, Cory Chamblin <ruby-forum@web.chamblin.info> wrote:

Jesús Gabriel y Galán wrote:

One liner:

mymethod(h = {:this => 'green'} && condition ? h.merge({:that =>
'blue'}) : h)

You could also do it this way:

method {:Apples => 1}.merge(condition ? {:Oranges => 2} : {})

mymethod(h = {:this => 'green'} && condition ? h.merge({:that =>
'blue'}) : h)

You could also do it this way:

method {:Apples => 1}.merge(condition ? {:Oranges => 2} : {})

For the love of all that is good and holy, please do not try to cram two
elegant lines of code into one disgusting line. That way lies madness...

:wink:

···

--
Posted via http://www.ruby-forum.com/\.

It's called Perl golf. It amazes and terrifies me how much it is still
played.

···

On 3/5/2010 2:36 PM, Nick Brown wrote:

mymethod(h = {:this => 'green'} && condition ? h.merge({:that =>
'blue'}) : h)
        

You could also do it this way:

method {:Apples => 1}.merge(condition ? {:Oranges => 2} : {})
      
For the love of all that is good and holy, please do not try to cram two
elegant lines of code into one disgusting line. That way lies madness...

:wink:

I must say that I vastly prefer Sven's original formulation:

  mymethod :this => 'green, :that => ('blue' if condition)

to any of the alternatives that have been suggested since. It's too
bad that it doesn't quite do the job for him. Sven, are you sure you
absolutely can't tolerate an options hash with a nil value in it?

···

On 3/5/10, Walton Hoops <walton@vyper.hopto.org> wrote:

For the love of all that is good and holy, please do not try to cram two
elegant lines of code into one disgusting line. That way lies madness...

:wink:

It's called Perl golf. It amazes and terrifies me how much it is still
played.

to any of the alternatives that have been suggested since. It's too
bad that it doesn't quite do the job for him. Sven, are you sure you
absolutely can't tolerate an options hash with a nil value in it?

Yep, I need it with form_for in Rails which doesn't like :method => nil.

···

--
Posted via http://www.ruby-forum.com/\.

If you can't use the resource oriented form of form_for which lets
Rails figure out the method based on whether the resource is new or
existing, then why not handle it on the other side of the interface

def mymethod(options={})
    options[:method] ||= :put
    #...
end

···

On Fri, Mar 5, 2010 at 6:23 PM, Sven S. <svoop@delirium.ch> wrote:

to any of the alternatives that have been suggested since. It's too
bad that it doesn't quite do the job for him. Sven, are you sure you
absolutely can't tolerate an options hash with a nil value in it?

Yep, I need it with form_for in Rails which doesn't like :method => nil.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

If you can't use the resource oriented form of form_for which lets
Rails figure out the method based on whether the resource is new or
existing, then why not handle it on the other side of the interface

You're right. However, I was wondering if there were a solution that
would be both simple and stay in the view.

As a matter of fact, form_for figures the method out just fine and so
does semantic_form_for (from Formtastic). For admin backends, however,
I'm using the "show view" for deleting resources as well, so the method
should be :delete then. And that's when - and *only* when - I'd need to
pass it.

I like Cory's one liner, it's an acceptable solution for the time being.
Thanks, Cory!

···

--
Posted via http://www.ruby-forum.com/\.