16

My aim is to replace keys in a string with values in hash. I am doing it like this:

"hello %{name}, today is %{day}" % {name: "Tim", day: "Monday"}

If a key in the string in missing in the hash:

"hello %{name}, today is %{day}" % {name: "Tim", city: "Lahore"}

then it will throw an error.

KeyError: key{day} not found

Expected result should be:

"hello Tim, today is %{day}" or "hello Tim, today is "

Can someone guide me in a direction to replace only the matching keys without throwing any errors?

2
  • What's your expected result in the second case, i.e. if a key is missing? Commented Aug 23, 2017 at 9:38
  • Thanks for looking into it. Expected response can be "hello Tim, today is %{day}" or "hello Tim, today is ". I think second one will be preferred Commented Aug 23, 2017 at 9:41

4 Answers 4

26

Starting with Ruby 2.3, % honors default values set via default=:

hash = {name: 'Tim', city: 'Lahore'}
hash.default = ''

'hello %{name}, today is %{day}' % hash
#=> "hello Tim, today is "

or dynamic defaults set via default_proc=:

hash = {name: 'Tim', city: 'Lahore'}
hash.default_proc = proc { |h, k| "%{#{k}}" }

'hello %{name}, today is %{day}' % hash
#=> "hello Tim, today is %{day}"

Note that only the missing key i.e. :day is passed to the proc. It is therefore unaware of whether you use %{day} or %<day>s in your format string which might result in a different output:

'hello %{name}, today is %<day>s' % hash
#=> "hello Tim, today is %{day}"
Sign up to request clarification or add additional context in comments.

4 Comments

I am using ruby 2.1.3 and this is not working. Is there any other way to do the same?
@Abhishek you could use gsub to replace %{...} with the corresponding value.
This is cool but I've been noticing in Ruby 3.3.6 in the rails console it only seems to work for string literals, like it won't work on a string stored in a variable and then string_var % some_hash
@MichaelKMadison must be something else, String#% is just a method. It doesn't matter whether the receiver is given as a literal or a local variable.
4

you can set a default hash value :

h = {name: "Tim", city: "Lahore"}
h.default = "No key"
p "hello %{name}, today is %{day}" % h #=>"hello Tim, today is No key"

Comments

2

I have the hash keys with spaces and it worked after converting the keys to symbols.

Hash with String keys (It returned an error on interpolation):

hash = {"First Name" => "Allama", "Last Name" => "Iqbal"}

Converting the hash keys to Symbols worked for me:

hash = {:"First Name" => "Allama", :"Last Name" => "Iqbal"}
hash.default = ''

'The %{First Name} %{Last Name} was a great poet.' % hash

// The Allama Iqbal was a great poet.

Comments

0

I know this is old, but I ended up making a utility for my needs. Here is my Gist https://gist.github.com/Genkilabs/71b479e1b1300a581a85e047db1cbb43

  #@param String with %{key} interpolation fields
  #@param Hash or Rails Model
  #@returns String original with any keys replaced if they are in the hash, others ignored
  def self.interpolate str, obj
    #For the hash, we need to re-key the hash to be sure, and add default to skip missing interpolations
    safe_hash = (defined?(obj.attributes) ? obj.attributes : obj.to_h).symbolize_keys
    safe_hash.default_proc = proc{|h, k| "%{#{k}}" }
    #On the string side, safeguard any place ther is a symbol inside an interpolated field
    str.gsub('%{:', '%{') % safe_hash
  end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.