From: Nobuyoshi Nakada Date: 2009-02-13T22:19:41+09:00 Subject: [ruby-dev:37998] [Feature:1.9] {Array,Enumerable}#uniq_by, #uniq_by! なかだです。 ブロックの値にしたがって一意のものを選ぶ、Array#uniq_byというの はどうでしょうか。 Index: array.c =================================================================== --- array.c (revision 22100) +++ array.c (working copy) @@ -2875,13 +2875,40 @@ ary_add_hash(VALUE hash, VALUE ary) } -static VALUE -ary_make_hash(VALUE ary) +static inline VALUE +ary_tmp_hash_new(void) { VALUE hash = rb_hash_new(); - RBASIC(hash)->klass = 0; + return hash; +} + +static VALUE +ary_make_hash(VALUE ary) +{ + VALUE hash = ary_tmp_hash_new(); return ary_add_hash(hash, ary); } +static VALUE +ary_add_hash_by(VALUE hash, VALUE ary) +{ + long i; + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + VALUE v = rb_ary_elt(ary, i), k = rb_yield(v); + if (rb_hash_lookup2(hash, k, Qundef) == Qundef) { + rb_hash_aset(hash, k, v); + } + } + return hash; +} + +static VALUE +ary_make_hash_by(VALUE ary) +{ + VALUE hash = ary_tmp_hash_new(); + return ary_add_hash_by(hash, ary); +} + static inline void ary_recycle_hash(VALUE hash) @@ -3063,4 +3090,43 @@ rb_ary_uniq(VALUE ary) } +static int +push_value(st_data_t key, st_data_t val, st_data_t ary) +{ + rb_ary_push((VALUE)ary, (VALUE)val); + return ST_DELETE; +} + +static VALUE +rb_ary_uniq_by_bang(VALUE ary) +{ + VALUE hash; + long size; + + RETURN_ENUMERATOR(ary, 0, 0); + hash = ary_make_hash_by(ary); + size = RHASH_SIZE(hash); + if (RARRAY_LEN(ary) == size) return Qnil; + ary_resize_capa(ary, size); + ARY_SET_LEN(ary, 0); + st_foreach(RHASH_TBL(hash), push_value, ary); + ary_recycle_hash(hash); + return ary; +} + +static VALUE +rb_ary_uniq_by(VALUE ary) +{ + VALUE hash; + long size; + + RETURN_ENUMERATOR(ary, 0, 0); + hash = ary_make_hash_by(ary); + size = RHASH_SIZE(hash); + ary = ary_new(rb_obj_class(ary), size); + st_foreach(RHASH_TBL(hash), push_value, ary); + ary_recycle_hash(hash); + return ary; +} + /* * call-seq: @@ -3920,4 +3986,6 @@ Init_Array(void) rb_define_method(rb_cArray, "uniq", rb_ary_uniq, 0); rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0); + rb_define_method(rb_cArray, "uniq_by", rb_ary_uniq_by, 0); + rb_define_method(rb_cArray, "uniq_by!", rb_ary_uniq_by_bang, 0); rb_define_method(rb_cArray, "compact", rb_ary_compact, 0); rb_define_method(rb_cArray, "compact!", rb_ary_compact_bang, 0); Index: enum.c =================================================================== --- enum.c (revision 22100) +++ enum.c (working copy) @@ -1794,4 +1794,29 @@ enum_cycle(int argc, VALUE *argv, VALUE } +static VALUE +enum_uniq_by_i(VALUE i, VALUE hash, int argc, VALUE *argv) +{ + return rb_hash_aset(hash, rb_yield_values(argc, argv), i); +} + +static int +push_value(st_data_t key, st_data_t val, st_data_t ary) +{ + rb_ary_push((VALUE)ary, (VALUE)val); + return ST_DELETE; +} + +static VALUE +enum_uniq_by(VALUE obj) +{ + VALUE hash = rb_hash_new(), uniq; + + RBASIC(hash)->klass = 0; + rb_block_call(obj, id_each, 0, 0, enum_uniq_by_i, hash); + uniq = rb_ary_new2(RHASH_SIZE(hash)); + st_foreach(RHASH_TBL(hash), push_value, uniq); + return uniq; +} + /* * The Enumerable mixin provides collection classes with @@ -1853,4 +1878,5 @@ Init_Enumerable(void) rb_define_method(rb_mEnumerable, "drop_while", enum_drop_while, 0); rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1); + rb_define_method(rb_mEnumerable, "uniq_by", enum_uniq_by, 0); id_eqq = rb_intern("==="); -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦