ruby-changes:40423
From: nobu <ko1@a...>
Date: Mon, 9 Nov 2015 21:27:43 +0900 (JST)
Subject: [ruby-changes:40423] nobu:r52504 (trunk): dig
nobu 2015-11-09 21:27:26 +0900 (Mon, 09 Nov 2015) New Revision: 52504 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52504 Log: dig * array.c (rb_ary_dig): new method Array#dig. * hash.c (rb_hash_dig): new method Hash#dig. * object.c (rb_obj_dig): dig in nested arrays/hashes. [Feature #11643] Modified files: trunk/ChangeLog trunk/NEWS trunk/array.c trunk/hash.c trunk/internal.h trunk/object.c trunk/test/ruby/test_array.rb trunk/test/ruby/test_hash.rb Index: array.c =================================================================== --- array.c (revision 52503) +++ array.c (revision 52504) @@ -1302,7 +1302,7 @@ rb_ary_aref(int argc, const VALUE *argv, https://github.com/ruby/ruby/blob/trunk/array.c#L1302 * a.at(-1) #=> "e" */ -static VALUE +VALUE rb_ary_at(VALUE ary, VALUE pos) { return rb_ary_entry(ary, NUM2LONG(pos)); @@ -5532,6 +5532,29 @@ rb_ary_any_p(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L5532 } /* + * call-seq: + * ary.dig(idx, ...) -> object + * + * Retrieves the value object corresponding to the each <i>idx</i> + * objects repeatedly. + * + * a = [[1, [2, 3]]] + * + * a.dig(0, 1, 1) #=> 3 + * a.dig(0, 0, 0) #=> nil + */ + +VALUE +rb_ary_dig(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); + self = rb_ary_at(self, *argv); + if (!--argc) return self; + ++argv; + return rb_obj_dig(argc, argv, self, Qnil); +} + +/* * Arrays are ordered, integer-indexed collections of any object. * * Array indexing starts at 0, as in C or Java. A negative index is assumed @@ -5882,6 +5905,7 @@ Init_Array(void) https://github.com/ruby/ruby/blob/trunk/array.c#L5905 rb_define_method(rb_cArray, "bsearch", rb_ary_bsearch, 0); rb_define_method(rb_cArray, "bsearch_index", rb_ary_bsearch_index, 0); rb_define_method(rb_cArray, "any?", rb_ary_any_p, 0); + rb_define_method(rb_cArray, "dig", rb_ary_dig, -1); id_cmp = rb_intern("<=>"); id_random = rb_intern("random"); Index: ChangeLog =================================================================== --- ChangeLog (revision 52503) +++ ChangeLog (revision 52504) @@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Mon Nov 9 21:27:23 2015 Nobuyoshi Nakada <nobu@r...> + + * array.c (rb_ary_dig): new method Array#dig. + + * hash.c (rb_hash_dig): new method Hash#dig. + + * object.c (rb_obj_dig): dig in nested arrays/hashes. + [Feature #11643] + Mon Nov 9 18:00:47 2015 Yuki Nishijima <mail@y...> * gems/bundled_gems: Upgrade the did_you_mean gem to 1.0.0.beta3 Index: object.c =================================================================== --- object.c (revision 52503) +++ object.c (revision 52504) @@ -3144,6 +3144,52 @@ rb_f_hash(VALUE obj, VALUE arg) https://github.com/ruby/ruby/blob/trunk/object.c#L3144 return rb_Hash(arg); } +struct dig_method { + VALUE klass; + int basic; +}; + +static ID id_dig; + +static int +dig_basic_p(VALUE obj, struct dig_method *cache) +{ + VALUE klass = RBASIC_CLASS(obj); + if (klass != cache->klass) { + cache->klass = klass; + cache->basic = rb_method_basic_definition_p(klass, id_dig); + } + return cache->basic; +} + +VALUE +rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound) +{ + struct dig_method hash = {Qnil}, ary = {Qnil}; + + for (; argc > 0; ++argv, --argc) { + if (!SPECIAL_CONST_P(obj)) { + switch (BUILTIN_TYPE(obj)) { + case T_HASH: + if (dig_basic_p(obj, &hash)) { + obj = rb_hash_aref(obj, *argv); + continue; + } + break; + case T_ARRAY: + if (dig_basic_p(obj, &ary)) { + obj = rb_ary_at(obj, *argv); + continue; + } + } + } + obj = rb_check_funcall(obj, id_dig, argc, argv); + if (obj == Qundef) return notfound; + break; + } + return obj; +} + /* * Document-class: Class * @@ -3521,5 +3567,6 @@ void https://github.com/ruby/ruby/blob/trunk/object.c#L3567 Init_Object(void) { id_to_f = rb_intern_const("to_f"); + id_dig = rb_intern_const("dig"); InitVM(Object); } Index: hash.c =================================================================== --- hash.c (revision 52503) +++ hash.c (revision 52504) @@ -2691,6 +2691,29 @@ rb_hash_any_p(VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L2691 return ret; } +/* + * call-seq: + * hsh.dig(key, ...) -> object + * + * Retrieves the value object corresponding to the each <i>key</i> + * objects repeatedly. + * + * h = { foo: {bar: {baz: 1}}} + * + * h.dig(:foo, :bar, :baz) #=> 1 + * h.dig(:foo, :zot) #=> nil + */ + +VALUE +rb_hash_dig(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); + self = rb_hash_aref(self, *argv); + if (!--argc) return self; + ++argv; + return rb_obj_dig(argc, argv, self, Qnil); +} + static int path_tainted = -1; static char **origenviron; @@ -4114,6 +4137,7 @@ Init_Hash(void) https://github.com/ruby/ruby/blob/trunk/hash.c#L4137 rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0); rb_define_method(rb_cHash, "any?", rb_hash_any_p, 0); + rb_define_method(rb_cHash, "dig", rb_hash_dig, -1); /* Document-class: ENV * Index: NEWS =================================================================== --- NEWS (revision 52503) +++ NEWS (revision 52504) @@ -42,6 +42,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L42 * Array * Array#bsearch_index [Feature #10730] + * Array#dig [Feature #11643] * Enumerable @@ -57,6 +58,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L58 * Hash * Hash#fetch_values [Feature #10017] + * Hash#dig [Feature #11643] * IO Index: internal.h =================================================================== --- internal.h (revision 52503) +++ internal.h (revision 52504) @@ -653,6 +653,7 @@ VALUE rb_ary_last(int, const VALUE *, VA https://github.com/ruby/ruby/blob/trunk/internal.h#L653 void rb_ary_set_len(VALUE, long); void rb_ary_delete_same(VALUE, VALUE); VALUE rb_ary_tmp_new_fill(long capa); +VALUE rb_ary_at(VALUE, VALUE); size_t rb_ary_memsize(VALUE); #ifdef __GNUC__ #define rb_ary_new_from_args(n, ...) \ @@ -968,6 +969,7 @@ void rb_obj_copy_ivar(VALUE dest, VALUE https://github.com/ruby/ruby/blob/trunk/internal.h#L969 VALUE rb_obj_equal(VALUE obj1, VALUE obj2); VALUE rb_class_search_ancestor(VALUE klass, VALUE super); double rb_num_to_dbl(VALUE val); +VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound); struct RBasicRaw { VALUE flags; Index: test/ruby/test_array.rb =================================================================== --- test/ruby/test_array.rb (revision 52503) +++ test/ruby/test_array.rb (revision 52504) @@ -2651,6 +2651,12 @@ class TestArray < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_array.rb#L2651 end end + def test_dig + h = @cls[@cls[{a: 1}], 0] + assert_equal(1, h.dig(0, 0, :a)) + assert_nil(h.dig(1, 0)) + end + private def need_continuation unless respond_to?(:callcc, true) Index: test/ruby/test_hash.rb =================================================================== --- test/ruby/test_hash.rb (revision 52503) +++ test/ruby/test_hash.rb (revision 52504) @@ -1302,6 +1302,12 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1302 x = x end + def test_dig + h = @cls[a: @cls[b: [1, 2, 3]], c: 4] + assert_equal(1, h.dig(:a, :b, 0)) + assert_nil(h.dig(:c, 1)) + end + class TestSubHash < TestHash class SubHash < Hash def reject(*) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/