Is it possible to reference one element in a hash within another element in the same hash?
# Pseudo code
foo = { :world => "World", :hello => "Hello #{foo[:world]}" }
foo[:hello] # => "Hello World"
Indirectly perhaps...
foo = { :world => 'World', :hello => lambda { "Hello #{foo[:world]}" }}
puts foo[:hello].call
If you want to make values of some keys dependent on others:
foo = Hash.new{|h, k|
case k
when :hello; "Hello #{h[:world]}"
when :bye; "Bye #{h[:world]}"
end
}
foo[:world] = 'World'
foo[:hello] # => 'Hello World'
foo[:bye] # => 'Bye World'
foo[:world] = 'Heaven'
foo[:hello] # => 'Hello Heaven'
foo[:bye] # => 'Bye Heaven'
This can't be done directly because "" is strictly evaluated.
Use of a lazy-value generator (e.g. lambda/proc) with later evaluation is required.
Happy coding.
No.
At least not in one step. You could do something like:
foo = {world: "hello"}
foo[:hello] = "Hello #{foo[:world]}"
Sure you can!
options = { :deep => :stuff }
options.merge!(:options => options)
# It's the same at any depth...
options[:options][:options][:options][:options][:options]
#=> {:deep=>:stuff, :options=>{...}}
Neat, huh? The hash object in options has the same object_id as the value assigned to :options.
Object#tap gives you a nice way to complete the Object initialisation with extra self-references.
{ foo: 1 }.tap { |obj| obj[:bar] = obj.fetch(:foo)}
Related
Ruby noob here
I understand ruby does pass by reference for function parameters
However, I am getting the feeling this is slightly different from conventional c/c++ style pass by reference
Sample code:
def test1(str)
str += ' World!'
end
def test2(str)
str << ' World!'
end
str = 'Hello'
test1(str)
p str # Hello
test2(str)
p str # Hello World!
I would expect test1 to also return Hello World! if I were using references in c/c++.
This is simply out of curiosity -- any explanations would be appreciated
I understand ruby does pass by reference for function parameters
Ruby is strictly pass-by-value, always. There is no pass-by-reference in Ruby, ever.
This is simply out of curiosity -- any explanations would be appreciated
The simple explanation for why your code snippet doesn't show the result you would expect for pass-by-reference is that Ruby isn't pass-by-reference. It is pass-by-value, and your code snippet proves that.
Here is a small snippet that demonstrates that Ruby is, in fact, pass-by-value and not pass-by-reference:
#!/usr/bin/env ruby
def is_ruby_pass_by_value?(foo)
foo << <<~HERE
More precisely, it is call-by-object-sharing!
Call-by-object-sharing is a special case of pass-by-value,
where the value is always an immutable pointer to a (potentially mutable) value.
HERE
foo = 'No, Ruby is pass-by-reference.'
return
end
bar = ['Yes, of course, Ruby *is* pass-by-value!']
is_ruby_pass_by_value?(bar)
puts bar
# Yes, of course, Ruby *is* pass-by-value!,
# More precisely, it is call-by-object-sharing!
# Call-by-object-sharing is a special case of pass-by-value,
# where the value is always an immutable pointer to a (potentially mutable) value.
Ruby does however allow mutation of objects, it is not a purely functional language like Haskell or Clean.
In the first case a new object was created when you did str += ' World!'
str = "Hello"
=> "Hello"
str.object_id
=> 69867706917360
str += " World"
=> "Hello World"
str.object_id
=> 69867706885680
str = "Hello"
=> "Hello"
str.object_id
=> 69867706856200
str << " World"
=> "Hello World"
str.object_id
=> 69867706856200
str = "Hello"
=> "Hello"
str.object_id
=> 69867706786780
str.freeze
=> "Hello"
str << " World"
RuntimeError: can't modify frozen String
str += " World"
=> "Hello World"
"<<" is a Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.
So "<<" doesn't create a new string, str.contact("World") doesn't create a new string as well.
The method test1 doesn't have to do anything with the returned result , you can try this method :
def test1(str)
str.concat(' World!')
end
Look at the following adaption of your test, by showing the object_id of your object you can easily see if it is the same or not. Test1 returns another String object because of the += concatenation but it is not used afterward.
This looks like passed by reference but in reality it is the value of the pointer to the object that is passed. The best explenation I could find for this is here , the author calls it pass-reference-by-value
def test1(str)
p ["in test1 before", str.object_id]
str += ' World!'
p ["in test1 after", str.object_id]
str
end
def test2(str)
p ["in test2", str.object_id]
str << ' World!'
end
str = 'Hello'
p ["in main", str.object_id]
test1(str)
p str # Hello
p ["after test1", str.object_id]
test2(str)
p str
p ["after test2", str.object_id]
Gives
["in main", 12363600]
["in test1 before", 12363600] # the same object, so pointer to object passed by value
["in test1 after", 12362976] # returns a new object, the old is unchanged
"Hello"
["after test1", 12363600] # idem
["in test2", 12363600]
"Hello World!"
["after test2", 12363600]
# still the same object
STRING IS REFERENCED, in ruby except value like numbers, true, false, nil, others are referenced.
a = "hello"
b = a
a.replace("Hola")
p a # Hola
p b # Hola
You would wanna add the magic comment at the beginning.:
# frozen_string_literal: true
a = "hello"
b = a
a.replace("Hola") # can't modify frozen String: "hello" (FrozenError)
def test1(str)
str += ' World!'
end
operator += is a syntactic sugar in ruby. Expression a += b translates to a = a + b. Operator + applied on String instance creates new String, which is a concatenation of the two arguments. That is why str is not modified in the first case.
Also I'd like to correct your statement:
I understand ruby does pass by reference for function parameters
Actually ruby passes by reference every parameter except the "value types" - ie. values nil, true, false and instances of class Fixnum
I have a whole bunch of lines like this
api_data[:foo] = foo if foo
api_data[:bar] = bar if bar
api_data[:batz] = batz if batz
I want a terse, idiomatic way to assign only if !nil.
Thanks
If you only want to assign values that are non-nil:
api_data.merge!(
{
foo: foo,
bar: bar,
bart: barts
}.reject { |_,v|v.nil? }
)
This is a little messy, so you might want to make an extension to the Hash class that ignores nil assignments:
class PickyHash < Hash
def []=(k,v)
return if (v.nil?)
super
end
end
Then:
api_data = PickyHash.new
api_data[:foo] = 'foo'
api_data[:foo] = nil
api_data
#> {:foo=>"foo"}
api_data[:foo] = foo
api_data[:bar] = bar
api_data[:batz] = batz
api_data.reject! { |_, value| value.nil? }
would do the trick.
Can't say that I like this solution, but anyway:
Hash[[:foo, :bar, :baz].find_all { |var| eval var.to_s }.map { |var| [var, eval(var.to_s)] }]
You can do something like below:
["foo", "bar", "batz"].each do |v|
eval(v).tap {|val| api_data[v.to_sym] = val if val }
end
Here's one using Array#compact and Enumerable#each_with_object:
[foo, bar, batz].compact.each_with_object(api_data) {|elem, acc| acc.merge!({elem => elem})}
Where [foo, bar, batz].compact returns the input array eliminating nil, and you're merging each non-nil element from the input array to api_data hash passed in as the initial value to each_with_object.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I used gsub! to substitute a match with a hash key with its hash value. For example:
def replace_string(string = "#ReplaceMe[xyz]##ReplaceMe[123]#Hello")
generator_replacements = {
"#ReplaceMe[xyz]#" => "Time",
"#ReplaceMe[123]#" => "Date"
}
generator_replacements.each{
|generator, replacement|
string = string.gsub!(generator.to_s, replacement.to_s)
puts string
}
end
replace_string
outputs:
TimeDateHello
TimeDateHello
I don't understand why gsub! substituted all the hash keys at one go rather than with every iteration. When I try using gsub, it substitutes with every iteration:
Time#ReplaceMe[123]#Hello
TimeDateHello
Can somebody explain why this happens?
There is no difference in that regard. The each loop is executed element by element and neither gsub nor gsub! can foresee the future.
This code:
replacements = { 'foo' => 'hello', 'bar' => 'world' }
string = 'foo bar!'
replacements.each do |placeholder, value|
string = string.gsub(placeholder, value)
end
string #=> 'hello world!'
is equivalent to:
string = 'foo bar!'
string = string.gsub('foo', 'hello') #=> "hello bar!"
string = string.gsub('bar', 'world') #=> "hello world!"
string #=> 'hello world!'
with gsub! you could write:
string = 'foo bar!'
string.gsub!('foo', 'hello') #=> "hello bar!"
string.gsub!('bar', 'world') #=> "hello world!"
string #=> 'hello world!'
The main difference is that gsub! changes the receiver in-place, whereas gsub returns a new string (hence the need to assign it back to string).
To perform multiple replacements at once, you can pass a hash to gsub:
string = 'foo bar!'
string.gsub(/foo|bar/, { 'foo' => 'hello', 'bar' => 'world' })
#=> "hello world!"
The regular expression can also be generated programmatically:
replacements = { 'foo' => 'hello', 'bar' => 'world' }
string = 'foo bar!'
string.gsub(Regexp.union(replacements.keys), replacements)
#=> "hello world!"
I have edited your code so that it works as shown below:
def replace_string(generated_string = "#ReplaceMe[xyz]##ReplaceMe[123]#Hello")
generator_replacements = {
"#ReplaceMe[xyz]#" => "Time",
"#ReplaceMe[123]#" => "Date"
}
generator_replacements.each do |generator, replacement|
string = generated_string.gsub(generator.to_s, replacement.to_s)
puts string
end
end
With this code using gsub I get:
Time#ReplaceMe[123]#Hello
#ReplaceMe[xyz]#DateHello
and with gsub! I get:
Time#ReplaceMe[123]#Hello
TimeDateHello
The reason for this is that gsub! modifies the existing string where as gsub does not modify the existing string. Does this help answer your question?
Using Ruby 1.9.2
Problem
Compare the content, not the results, of two procs. I understand the results can't be tested because of the halting problem but that's OK; I don't want to test the results anyway.
For instance
proc {#x == "x"} == proc {#x == "x"} => false # doh!
That returns false because the objects inside the procs are not the same.
My clunky solution
I have a work around solution that kinda sorta does what I want but it doesn't really test that the proc is "equal" to what I put in it. In my specific case the format of my procs will always be boolean tests on instance variables like this:
{#x == "x" && #y != "y" || #z == String}
I wrote a method that builds classes dynamically and creates instance variables set to specified values:
def create_proc_tester(property_value_hash)
new_class = Class.new.new
new_class.class.class_eval do
define_method(:xql?) { |&block| instance_eval &block }
end
property_value_hash.each do |key, value|
new_class.instance_variable_set("##{key}", value)
end
new_class
end
Which could be used something like this:
class Foo
attr_accessor :block
end
foo = Foo.new
foo.block = proc {#x == "x" && #y != "y" || #z == String}
tester = create_proc_tester(:x => "x", :y => "y", :z => Fixnum)
puts "Test #1: #{tester.xql? &foo.block}"
tester = create_proc_tester(:x => "x", :y => "x", :z => String)
puts "Test #2: #{tester.xql? &foo.block}"
> Test #1: false
> Test #2: true
.
.
That's all great and wonderful but I want to know if there is a better, more meta, way to do this that actually tests the contents of the proc not just a work around that solves my specific problem; something that could be used to test any proc.
I was thinking there might be a way to use the Ruby parser to get something to compare but I have no idea how. I'm researching it now but thought I'd try to see if anyone here has done this before and knows how. That might be a dead-end though because of the dynamic nature of Ruby but that's where I'm looking now.
If you're using Ruby 1.9, you may be able to use the sourcify gem.
$ irb
> require 'sourcify'
=> true
> a = proc {#x == "x"}
=> #<Proc:0x9ba4240#(irb):2>
> b = proc {#x == %{x}}
=> #<Proc:0x9ba23f0#(irb):3>
> a == b
=> false
> a.to_source == b.to_source
=> true
> RUBY_VERSION
=> "1.9.2"
We also ran into the ParseTree/Ruby 1.9 incompatibility problem at my company.
$ sudo gem install ruby2ruby ParseTree
require 'parse_tree'
require 'ruby2ruby'
require 'parse_tree_extensions'
# All of these are the same:
proc { puts 'a' }.to_ruby # => "proc { puts(\"a\") }"
lambda { puts "a" }.to_ruby # => "proc { puts(\"a\") }"
Proc.new { puts %{a} }.to_ruby # => "proc { puts(\"a\") }"
# If you need to do this with classes:
class Bar; define_method(:foo) { 'a' }; end
puts Ruby2Ruby.new.process(Unifier.new.process(ParseTree.translate(Bar)))
# will print this:
# class Bar < Object
# def foo
# "a"
# end
# end
From example:
local_var = "Thanks!"
#instance_var = "Thank you ,too"
Then how can I get the local_var and instance_var part by them self.
I mean weather there is a method maybe called get_self_name to get the name of himself:
local_var.get_self_name # => 'local_var'
#instance_var.get_self_name # => '#instance_var' or => 'instance_var'
a = 'abc'
a.get_self_name # => 'a'
$ irb
>> local_var = "foo"
=> "foo"
>> #instance_var = "bar"
=> "bar"
>> instance_variables
=> ["#prompt", "#instance_var"]
>> local_variables
=> ["_", "local_var"]
You also may want to check out ObjectSpace module.
The _ local variable is automatically set to returned value of the last irb statement. #prompt is probably irb's prompt format or something similar.
There is no method that can do that. Here are some ways to get around it:
-1- Use a hash:
local_var = "Thanks!"
#instance_var = "Thank you ,too"
hash = {
local_var: local_var,
instance_var: #instance_var
}
hash.index(#instance_var) #=> :instance_var
-2- use instance_variables:
local_var = "Thanks!"
#instance_var = "Thank you ,too"
instance_variables.find {|x| instance_variable_get(x) == #instance_var } #=> :instance_var
Note that this won't work for local variables.