0

ruby 2.1.1

Is there a way to do the logic in this piece of code in one line or a more concise manner?

user = User.new
h = Hash.new
attrs = [:name, :foo, :bar]
attrs.each do |a|
    h[a] = user[a] if user.has_attribute? a
end
return h
Derek
  • 11,980
  • 26
  • 103
  • 162
  • From a matter of style, `Hash.new` with no options is almost always written as simply `{ }`. It's also unnecessary to use `return r` when `r` at the end of the method has exactly the same effect. The only place `return` is normally used is to break out of a method early. – tadman May 31 '14 at 22:51
  • Are you using Rails ? – Arup Rakshit May 31 '14 at 23:05
  • @ArupRakshit yes, I am using `Rails 4.1.1` – Derek May 31 '14 at 23:10
  • tag your question also then... – Arup Rakshit May 31 '14 at 23:10
  • @ArupRakshit k, tagged, at first I thought ruby would have a complete solution to this issue. Didn't think rails would have such a better alternative. Now I know, thanks! – Derek May 31 '14 at 23:14

3 Answers3

3

If you're using Rails and User is an ActiveRecord model (which it looks like given your use of has_attribute?) then this will do the same thing:

user = User.new
...
return user.attributes.slice("name", "foo", "bar")

Or, if you really want symbols:

return user.attributes.with_indifferent_access.slice(:name, :foo, :bar)
gwcoffey
  • 5,551
  • 1
  • 18
  • 20
1

It seems you are on Rails. If so,then -

attrs = [:name, :foo, :bar]
# the result hash will be returned, if last line of the method.
user.attributes.extract!(*attrs) 

Look these methods extract! and attributes.

Example :

arup@linux-wzza:~/Rails/app> rails c
Loading development environment (Rails 4.1.1)
2.0.0-p451 :001 > h = { a: 1, b: 2, c: 3, d: 4 }
 => {:a=>1, :b=>2, :c=>3, :d=>4}
2.0.0-p451 :002 > h.extract!(:a ,:b ,:x)
 => {:a=>1, :b=>2}
2.0.0-p451 :003 >
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
0

Answers above are correct in Rails scope, I'l just add generic solution:

# assuming user[a] returns nil, if user have no a attribute
[:name, :foo, :bar].
  map{|a| [attr, user[a]]}.
  reject{|k, v| v.nil?}.
  to_h

# assuming user[a] can raise if not user.has_attribute?(a)
[:name, :foo, :bar].
   map{|a| [attr, user.has_attribute?(a) && user[a]]}.
   reject{|k, v| !v}.
   to_h

I've formatted them as NOT one-liners, but they are still one-statements :)

Basically, the trick is "invent the right method chain to convert one sequence to other", and requires to know all Enumerable sequence-transforming methods (map/select/reduce/reject/...), as well as a method to transform array of key-value pairs into hash (#to_h is standard in Ruby 2.1.1)

zverok
  • 1,290
  • 1
  • 9
  • 13