Andrey Deineko
Andrey Deineko

Reputation: 52357

Create key-value in a new hash only if it is present in an old hash

conditions[:foo] = params[:foo] if params[:foo].present?

It will only create a key-value pair in the conditions hash if the key-value pair is present in params.

Is there a way to write it shorter? I was thinking about Hash#fetch:

conditions[:foo] = params.fetch(:foo, nil)

But it is not what I want, because then I'll get a pair with nil value, if there is no such key in params.

I only need the conditions key-value created if such a key-value is present in params.

I am asking the question because I have a few similar lines of assigning, where params[:foo] is repeated twice on each line:

  conditions[:foo] = params[:foo] if params[:foo].present?
  conditions[:bar] = params[:bar] if params[:bar].present?
  conditions[:baz] = params[:baz] if params[:baz].present?
  conditions[:zxc] = params[:zxc] if params[:zxc].present?
  # ...

Upvotes: 3

Views: 2133

Answers (5)

Andy Jones
Andy Jones

Reputation: 1104

I think the only problem with your code is the repetition. Perhaps this is a bit contentious, but I would DRY it up with a lambda.

lAdd ->(k) { conditions[k] = params[k] if params[k].present? }

lAdd.(:foo)
lAdd.(:bar)
lAdd.(:baz)
lAdd.(:zxc)

Of course if you find yourself doing this in multiple places, it should be a common helper function, rather than a lambda that hardcodes the two hashes.

Upvotes: 1

Wand Maker
Wand Maker

Reputation: 18762

Here is one alternate way to do this:

require "active_support/all"

params = {foo: "", bar: "value", baz: nil, zxc: false}
conditions = {}

keys = %i(foo bar baz zxc)

present_params = params.slice(*keys).select{|_,v| v.present? }
conditions.merge!(present_params)

Upvotes: 0

Frederick Cheung
Frederick Cheung

Reputation: 84114

First off, in your original code I would use params.key? (unless you really don't care about cases where the value is false, empty array etc). I would probably stick with that since it is very clear and not that verbose.

One alternative I can think of:

conditions.merge!(params.slice(:foo))

This requires active support, and would stay concise even if you did this with many hash keys - you would just add them to the call to slice.

slice uses has_key? to return only the desired portion of the hash so handles values like false and [] as I suggest rather than as your question implements them.

Upvotes: 3

Andrey Deineko
Andrey Deineko

Reputation: 52357

I asked the question because I had few similar lines of assigning, where params[:foo] is repeated twice on each line:

  conditions[:foo] = params[:foo] if params[:foo].present?
  conditions[:bar] = params[:bar] if params[:bar].present?
  conditions[:baz] = params[:baz] if params[:baz].present?
  conditions[:zxc] = params[:zxc] if params[:zxc].present?
  # ...

What I came up to shorten what I had is the following:

  %i(foo baz bar zxc).each do |value|
    conditions[value] = params[value] if params[value].present?
  end

But I thought it is much less readable and wanted to stick with a new line for each key-value assignment, but in shorter form.

Upvotes: 0

zwippie
zwippie

Reputation: 15515

Since you need a conditional assignment, I think your first version is pretty concise.

Upvotes: 1

Related Questions