Reputation: 7387
I have a nested hash in ruby like this
a = {
'a': 1,
'b': 2,
'c': {
'd': 3
}
}
=> {:a=>1, :b=>2, :c=>{:d=>3}}
and I set a.default = ''
How could I get the value of d
if I use a string interpolation expression like:
puts "%{c['d']}" % a
I have unsuccessfully tried
puts "%{c}" % a
{:d=>3}
=> nil
puts "%{c['d']}" % a
=> nil
puts "%{c[:d]}" % a
=> nil
I would need some way to get the nested 3 in a['c']['d']
. The two previous examples would suit me but they return empty string.
ps. If I don't use the a.default = ''
I get the error
puts "%{c[:d]}" % a
KeyError: key{c[:d]} not found
from (pry):45:in `%'
**p.s: I'm using pry to run the code
Upvotes: 1
Views: 352
Reputation: 3811
class String
alias old_interpolation %
def %(x)
if x.is_a? Hash
path = self.split('/').map(&:to_sym)
begin
x.dig(*path)
rescue => error
end
else
old_interpolation x
end
end
end
# input, note: no set `a.default = ''`
a = {
'a': 1,
'b': 2,
'c': {
'd': 3
}
}
# output
puts "c" % a # {:d=>3}
puts "c/d" % a # 3
puts "a/d" % a # error -> return nil
You can change the separator '/' to what you want :D
update If you want to interpolate string, not just only get hash value
class String
alias old_interpolation %
def %(x)
if x.is_a? Hash
self.scan(/(?<=%{)[^}]*(?=})/).inject(self) do |result, match|
keys = match.split('/').map(&:to_sym)
begin
value = x.dig(*keys)
rescue => error
# what should do here ?
ensure
result = result.sub(/%{[^}]*}/, value.to_s)
end
result
end
else
old_interpolation x
end
end
end
puts "here %{c/d} we %{a} are" % a # here 3 we 1 are
Upvotes: 2
Reputation: 1309
I looked at the Ruby source code for sprintf
and this does not appear to be possible. The relevant section is reproduced below.
case '<':
case '{':
{
const char *start = p;
char term = (*p == '<') ? '>' : '}';
int len;
for (; p < end && *p != term; ) {
p += rb_enc_mbclen(p, end, enc);
}
if (p >= end) {
rb_raise(rb_eArgError, "malformed name - unmatched parenthesis");
}
#if SIZEOF_INT < SIZEOF_SIZE_T
if ((size_t)(p - start) >= INT_MAX) {
const int message_limit = 20;
len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start);
rb_enc_raise(enc, rb_eArgError,
"too long name (%"PRIuSIZE" bytes) - %.*s...%c",
(size_t)(p - start - 2), len, start, term);
}
#endif
len = (int)(p - start + 1); /* including parenthesis */
if (sym != Qnil) {
rb_enc_raise(enc, rb_eArgError, "named%.*s after <%"PRIsVALUE">",
len, start, rb_sym2str(sym));
}
CHECKNAMEARG(start, len, enc);
get_hash(&hash, argc, argv);
sym = rb_check_symbol_cstr(start + 1,
len - 2 /* without parenthesis */,
enc);
if (!NIL_P(sym)) nextvalue = rb_hash_lookup2(hash, sym, Qundef);
if (nextvalue == Qundef) {
if (NIL_P(sym)) {
sym = rb_sym_intern(start + 1,
len - 2 /* without parenthesis */,
enc);
}
nextvalue = rb_hash_default_value(hash, sym);
if (NIL_P(nextvalue)) {
rb_key_err_raise(rb_enc_sprintf(enc, "key%.*s not found", len, start), hash, sym);
}
}
if (term == '}') goto format_s;
p++;
goto retry;
}
Upvotes: 0