Hi,

At Mon, 25 Apr 2005 16:36:21 +0900,
Daniel Amelang wrote in [ruby-talk:139707]:
> So why not go back to David's suggestion of adding a delete_at method
> for the case of multiple values? And have it _always_ return an array.

Because Array already has delete_at which takes just one
argument.  It feels similar enough to cause confusion, doesn't
it?


Index: hash.c =================================================================== RCS file: /cvs/ruby/src/ruby/hash.c,v retrieving revision 1.148 diff -U2 -p -u -r1.148 hash.c --- hash.c 2 Apr 2005 04:23:56 -0000 1.148 +++ hash.c 25 Apr 2005 09:34:45 -0000 @@ -625,9 +625,8 @@ rb_hash_index(hash, value) * hsh.delete(key) {| key | block } => value * - * Deletes and returns a key-value pair from <i>hsh</i> whose key is - * equal to <i>key</i>. If the key is not found, returns the - * <em>default value</em>. If the optional code block is given and the - * key is not found, pass in the key and return the result of - * <i>block</i>. + * Deletes and returns a value from <i>hsh</i> whose key is equal to + * <i>key</i>. If the key is not found, returns the +nil+. If the + * optional code block is given and the key is not found, pass in the + * key and return the result of <i>block</i>. * * h = { "a" => 100, "b" => 200 } @@ -659,4 +658,88 @@ rb_hash_delete(hash, key) } +/* + * call-seq: + * hsh.remove!(key1, ...) => [value1, ...] + * hsh.remove!(key1, ...) {| key | block } => [value1, ...] + * + * Deletes and returns a value from <i>hsh</i> whose key is equal to + * <i>key</i>. If the key is not found, returns the +nil+. If the + * optional code block is given and the key is not found, pass in the + * key and return the result of <i>block</i>. + * + * h = { "a" => 100, "b" => 200 } + * h.remove!("a", "b") #=> [100, 200] + * h = { "a" => 100, "b" => 200 } + * h.remove!("z", "a") #=> [nil, 100] + * h.remove!("z") { |el| "#{el} not found" } #=> "z not found" + * + */ + +VALUE +rb_hash_remove_bang(argc, argv, hash) + int argc; + VALUE *argv; + VALUE hash; +{ + int i; + VALUE ret = rb_ary_new2(argc); + + rb_hash_modify(hash); + for (i = 0; i < argc; ++i) { + st_data_t key = (st_data_t)argv[i], val; + if (RHASH(hash)->iter_lev > 0) { + if (st_delete_safe(RHASH(hash)->tbl, &key, &val, Qundef)) { + FL_SET(hash, HASH_DELETED); + rb_ary_push(ret, (VALUE)val); + continue; + } + } + else if (st_delete(RHASH(hash)->tbl, &key, &val)) { + rb_ary_push(ret, (VALUE)val); + continue; + } + if (rb_block_given_p()) { + rb_ary_push(ret, rb_yield((VALUE)key)); + } + } + return ret; +} + +/* + * call-seq: + * hsh.remove(key1, ...) => newhsh + * hsh.remove(key1, ...) {| key | block } => newhsh + * + * Returns a copy of <i>hsh</i> which doesn't contain pairs whose key + * is equal to any given <i>key</i>s. If the optional code block is + * given and the key is not found, pass in the key. + * + * h = { "a" => 100, "b" => 200 } + * h.remove!("a", "b") #=> [100, 200] + * h = { "a" => 100, "b" => 200 } + * h.remove!("z", "a") #=> [nil, 100] + * h.remove!("z") { |el| "#{el} not found" } #=> "z not found" + * + */ + +VALUE +rb_hash_remove(argc, argv, hash) + int argc; + VALUE *argv; + VALUE hash; +{ + int i; + + hash = rb_obj_dup(hash); + for (i = 0; i < argc; ++i) { + st_data_t key = (st_data_t)argv[i], val; + if (!st_delete(RHASH(hash)->tbl, &key, &val) && + rb_block_given_p()) { + rb_yield((VALUE)key); + } + } + return hash; +} + struct shift_var { int stop; @@ -2527,4 +2610,6 @@ Init_Hash() rb_define_method(rb_cHash,"reject", rb_hash_reject, 0); rb_define_method(rb_cHash,"reject!", rb_hash_reject_bang, 0); + rb_define_method(rb_cHash,"remove!", rb_hash_remove_bang, -1); + rb_define_method(rb_cHash,"remove", rb_hash_remove, -1); rb_define_method(rb_cHash,"clear", rb_hash_clear, 0); rb_define_method(rb_cHash,"invert", rb_hash_invert, 0);
-- Nobu Nakada