What is the Ruby equivalent of PHP compact?

Given some local variables , what would be the easiest way to compact them in Ruby?

 def foo name = 'David' age = 25 role = :director ... # How would you build this: # { :name => 'David', :age => 25, :role => :director } # or # { 'name' => 'David', 'age' => 25, 'role' => :director } end 

In PHP, I can just do this:

 $foo = compact('name', 'age', 'role'); 
+6
source share
2 answers

I came up with a significant improvement in my initial answer. It is cleaner if you inherit from Binding . to_sym exists because older versions of ruby ​​have local_variables as strings.

instance method

 class Binding def compact( *args ) compacted = {} locals = eval( "local_variables" ).map( &:to_sym ) args.each do |arg| if locals.include? arg.to_sym compacted[arg.to_sym] = eval( arg.to_s ) end end return compacted end end 

Using

 foo = "bar" bar = "foo" binding.compact( "foo" ) # => {:foo=>"bar"} binding.compact( :bar ) # => {:bar=>"foo"} 

Original answer

This is the closest I could go to a method that behaves like a php compact -

method

 def compact( *args, &prok ) compacted = {} args.each do |arg| if prok.binding.send( :eval, "local_variables" ).include? arg compacted[arg.to_sym] = prok.binding.send( :eval, arg ) end end return compacted end 

usage example

 foo = "bar" compact( "foo" ){} # or compact( "foo", &proc{} ) 

This is not ideal because you need to go through proc. I am open to suggestions on how to improve this.

+9
source

This is a variant of Bungus' answer, but here is a single-line font that is clearly uglier but doesn't extend the binding or anything else:

 foo = :bar baz = :bin hash = [:foo, :baz].inject({}) {|h, v| h[v] = eval(v.to_s); h } # hash => {:baz=>:bin, :foo=>:bar} 

You can also make it look like sorting like a method call, abusing the block binding - again, a variant with the original Bungus answer:

 module Kernel def compact(&block) args = block.call.map &:to_sym lvars = block.binding.send(:eval, "local_variables").map &:to_sym (args & lvars).inject({}) do |h, v| h[v] = block.binding.send(:eval, v.to_s); h end end end foo = :bar baz = :bin compact {[ :foo, :bar, :baz ]} # {:foo=>:bar, :baz=>:bin} 

(I will just tell myself that {[..]} is a garbage compactor.)

If you use the binding_of_caller , you can refuse proc and explicitly bind all together:

 require 'binding_of_caller' module Kernel def compact(*args) lvars = binding.of_caller(1).send(:eval, "local_variables").map &:to_sym (args.map(&:to_sym) & lvars).inject({}) do |h, v| h[v] = binding.of_caller(2).send(:eval, v.to_s); h end end end foo = :bar baz = :bin compact :foo, :bar, :baz # {:foo=>:bar, :baz=>:bin} 

Be careful, this is slow. In production code, you probably never try to do this, but instead just keep a hash of values, so the programmer should support this after you don't track you down and kill you in a dream.

+1
source

Source: https://habr.com/ru/post/948530/


All Articles