mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Merge faec0370cd
into d025bc230c
This commit is contained in:
commit
585353a0bc
2 changed files with 104 additions and 0 deletions
64
hash.c
64
hash.c
|
@ -4902,6 +4902,69 @@ rb_hash_dig(int argc, VALUE *argv, VALUE self)
|
|||
return rb_obj_dig(argc, argv, self, Qnil);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* h.safe_dig(key, ...) -> object or nil
|
||||
*
|
||||
* Traverses the given keys and returns the value found or +nil+ if any step
|
||||
* is not a Hash or Array, or the key/index is missing.
|
||||
*
|
||||
* Accepts both String and Symbol keys for Hash lookup.
|
||||
*
|
||||
* { foo: { bar: 1 } }.safe_dig(:foo, :bar) #=> 1
|
||||
* { foo: "none" }.safe_dig(:foo, :bar) #=> nil
|
||||
* { items: [{id: 1}, {id: 2}] }.safe_dig(:items, 1, :id) #=> 2
|
||||
*
|
||||
*/
|
||||
static VALUE
|
||||
rb_safe_hash_dig(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
||||
VALUE curr = self;
|
||||
|
||||
for (; argc > 0; argv++, argc--)
|
||||
{
|
||||
VALUE key = *argv, val = Qundef;
|
||||
|
||||
if (RB_TYPE_P(curr, T_HASH))
|
||||
{
|
||||
val = rb_hash_lookup2(curr, key, Qundef);
|
||||
if (val == Qundef)
|
||||
{
|
||||
if (RB_TYPE_P(key, T_SYMBOL))
|
||||
{
|
||||
val = rb_hash_lookup2(curr, rb_sym2str(key), Qundef);
|
||||
}
|
||||
else if (RB_TYPE_P(key, T_STRING))
|
||||
{
|
||||
val = rb_hash_lookup2(curr, ID2SYM(rb_intern_str(key)), Qundef);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (RB_TYPE_P(curr, T_ARRAY))
|
||||
{
|
||||
if (RB_INTEGER_TYPE_P(key))
|
||||
{
|
||||
val = rb_ary_entry(curr, NUM2LONG(key));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
if (val == Qundef) return Qnil;
|
||||
if (argc == 1) return val;
|
||||
if (NIL_P(val)) return Qnil;
|
||||
curr = val;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
static int
|
||||
hash_le_i(VALUE key, VALUE value, VALUE arg)
|
||||
{
|
||||
|
@ -7468,6 +7531,7 @@ Init_Hash(void)
|
|||
|
||||
rb_define_method(rb_cHash, "any?", rb_hash_any_p, -1);
|
||||
rb_define_method(rb_cHash, "dig", rb_hash_dig, -1);
|
||||
rb_define_method(rb_cHash, "safe_dig", rb_safe_hash_dig, -1);
|
||||
|
||||
rb_define_method(rb_cHash, "<=", rb_hash_le, 1);
|
||||
rb_define_method(rb_cHash, "<", rb_hash_lt, 1);
|
||||
|
|
40
spec/ruby/core/hash/safe_dig_spec.rb
Normal file
40
spec/ruby/core/hash/safe_dig_spec.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
require_relative '../../spec_helper'
|
||||
|
||||
describe "Hash#safe_dig" do
|
||||
it "returns the value for a valid path" do
|
||||
h = { foo: { bar: "baz" } }
|
||||
h.safe_dig(:foo, :bar).should == "baz"
|
||||
end
|
||||
|
||||
it "returns nil when an intermediate value is not a hash or array" do
|
||||
h = { foo: "value" }
|
||||
h.safe_dig(:foo, :bar).should == nil
|
||||
end
|
||||
|
||||
it "returns nil when the index is out of bounds" do
|
||||
h = { items: [1, 2, 3] }
|
||||
h.safe_dig(:items, 5).should == nil
|
||||
end
|
||||
|
||||
it "works with arrays in the path" do
|
||||
h = { items: [{ id: 1 }, { id: 2 }] }
|
||||
h.safe_dig(:items, 1, :id).should == 2
|
||||
end
|
||||
|
||||
it "accepts string and symbol keys" do
|
||||
h = { "user" => { "name" => "Dani" } }
|
||||
h.safe_dig(:user, :name).should == "Dani"
|
||||
end
|
||||
|
||||
it "returns nil when receiver key is nil" do
|
||||
h = { user: nil }
|
||||
h.safe_dig(:user, :name).should == nil
|
||||
end
|
||||
|
||||
it "does not modify the receiver" do
|
||||
h = { foo: { bar: "baz" } }
|
||||
original = h.dup
|
||||
h.safe_dig(:foo, :bar)
|
||||
h.should == original
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue