diff --git a/hash.c b/hash.c index d2ca057e33..a26bfeec2f 100644 --- a/hash.c +++ b/hash.c @@ -1185,6 +1185,41 @@ rb_hash_delete_m(VALUE hash, VALUE key) } } +/* + * call-seq: + * hsh.delete!(key) -> value + * + * Deletes the key-value pair and returns the value from hsh whose + * key is equal to key. If the key is not found, it raises a + * KeyError error. + * + * h = { "a" => 100, "b" => 200 } + * h.delete!("a") #=> 100 + * h.delete!("z") #=> KeyError + * + */ + +static VALUE +rb_hash_delete_bang_m(VALUE hash, VALUE key) +{ + VALUE val; + + rb_hash_modify_check(hash); + val = rb_hash_delete_entry(hash, key); + + if (val != Qundef) { + return val; + } + else { + VALUE desc = rb_protect(rb_inspect, key, 0); + if (NIL_P(desc)) { + desc = rb_any_to_s(key); + } + desc = rb_str_ellipsize(desc, 65); + rb_key_err_raise(rb_sprintf("key not found: %"PRIsVALUE, desc), hash, key); + } +} + struct shift_var { VALUE key; VALUE val; @@ -4686,6 +4721,7 @@ Init_Hash(void) rb_define_method(rb_cHash, "shift", rb_hash_shift, 0); rb_define_method(rb_cHash, "delete", rb_hash_delete_m, 1); + rb_define_method(rb_cHash, "delete!", rb_hash_delete_bang_m, 1); rb_define_method(rb_cHash, "delete_if", rb_hash_delete_if, 0); rb_define_method(rb_cHash, "keep_if", rb_hash_keep_if, 0); rb_define_method(rb_cHash, "select", rb_hash_select, 0); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 55e46584b4..279c9f3f06 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -383,6 +383,23 @@ def test_delete assert_equal('default 99', h1.delete(99) {|i| "default #{i}" }) end + def test_delete! + h1 = @cls[ 1 => 'one', 2 => 'two', true => 'true' ] + h2 = @cls[ 1 => 'one', 2 => 'two' ] + h3 = @cls[ 2 => 'two' ] + + assert_equal('true', h1.delete!(true)) + assert_equal(h2, h1) + + assert_equal('one', h1.delete!(1)) + assert_equal(h3, h1) + + assert_equal('two', h1.delete!(2)) + assert_equal(@cls[], h1) + + assert_raise(KeyError) { h1.delete!(99) } + end + def test_delete_if base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ] h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]