Closed rudy-on-rails closed 6 years ago
I think it should be named deep_has_key?
or has_deep_key?
instead.
Count me in with this!
It uses recursion, but I think it is a good start:
VALUE rb_hash_deep_has_key(VALUE hash, VALUE key)
{
VALUE values;
long i;
if (rb_hash_has_key(hash, key) == Qtrue)
return Qtrue;
values = env_values();
for (i=0; i<RARRAY_LEN(values); i++) {
if (rb_hash_deep_has_key(RARRAY_AREF(values, i), key) == Qtrue)
return Qtrue;
}
return Qfalse;
}
Can be tested by:
def test_deep_has_key
z = @cls[@cls[1=>2]]
assert_send([z, :deep_has_key?, 1])
assert_equal(true, z.deep_has_key?(1))
end
Try:
{
:x => 3,
:y => [1,2,3].
:z => {
:a => 3
}
}
And search for :a
...
Some infinite recursion happens when:
z = @cls[3=>@cls[1=>2]]
assert_send([z, :deep_has_key?, 1])
is ran
There was a missing type test for the hash value:
VALUE rb_hash_deep_has_key(VALUE hash, VALUE key)
{
VALUE values;
long i;
if (!RB_TYPE_P(hash, T_HASH))
return Qfalse;
if (rb_hash_has_key(hash, key) == Qtrue)
return Qtrue;
values = env_values();
for (i=0; i<RARRAY_LEN(values); i++) {
if (rb_hash_deep_has_key(RARRAY_AREF(values, i), key) == Qtrue)
return Qtrue;
}
return Qfalse;
}
Oh my bad, rb_hash_values
instead of env_values
Non-recursive implementation:
VALUE rb_hash_deep_has_key(VALUE hash, VALUE key)
{
VALUE values;
VALUE item;
VALUE stack;
VALUE children;
long i;
if (!RB_TYPE_P(hash, T_HASH))
return Qfalse;
stack = rb_hash_values(hash);
while(RARRAY_LEN(stack) > 0) {
item = rb_ary_pop(stack);
if (RB_TYPE_P(item, T_HASH)) {
if (rb_hash_has_key(item, key) == Qtrue)
return Qtrue;
children = rb_hash_values(hash);
if (RB_TYPE_P(children, T_ARRAY)) {
for (i = 0; i < RARRAY_LEN(children); i++) {
rb_ary_push(stack, RARRAY_AREF(children, i));
}
}
}
}
return Qfalse;
}
Cleaning up a bit and adding more tests:
rb_hash_deep_has_key(VALUE hash, VALUE key)
{
VALUE item;
VALUE stack;
VALUE children;
long i;
if (!RB_TYPE_P(hash, T_HASH))
return Qfalse;
stack = rb_hash_values(hash);
while(RARRAY_LEN(stack) > 0) {
item = rb_ary_pop(stack);
if (RB_TYPE_P(item, T_HASH)) {
if (rb_hash_has_key(item, key) == Qtrue)
return Qtrue;
children = rb_hash_values(hash);
if (RB_TYPE_P(children, T_ARRAY)) {
for (i = 0; i < RARRAY_LEN(children); i++) {
rb_ary_push(stack, RARRAY_AREF(children, i));
}
}
}
}
return Qfalse;
}
def test_deep_has_key
x = @cls[3=>@cls[1=>2]]
y = @cls[x: 3, y: [1,2,3], z: @cls[a: 3]]
z = @cls[]
assert_send([x, :deep_has_key?, 1])
assert_send([y, :deep_has_key?, :a])
assert_equal(false, z.deep_has_key?(:any))
end
The
deep_search
param can be used to recursively search withinvalues
of the hash object, when applicable