Suggestion: Hash.remove

A

Andrew Walrond

Something I often find useful is a version of Hash.delete which returns the
hash rather than the deleted item. Useful for terse chained code.

class Hash
def remove!(*some_keys)
some_keys.each { |key| delete(key) }
return self
end
def remove(*some_keys)
return dup.remove!(*some_keys)
end
end


Perhaps something like it is already planned for the next version? If not,
perhaps the Hash maintainer might consider something like it.

Andrew Walrond
 
L

Lyndon Samson

Something I often find useful is a version of Hash.delete which returns the
hash rather than the deleted item. Useful for terse chained code.

class Hash
def remove!(*some_keys)
some_keys.each { |key| delete(key) }
return self
end
def remove(*some_keys)
return dup.remove!(*some_keys)
end
end

Perhaps something like it is already planned for the next version? If not,
perhaps the Hash maintainer might consider something like it.

I think there is a bias against method chaining by the Ruby Powers-that-Be.
 
D

David A. Black

Hi --

I think there is a bias against method chaining by the Ruby Powers-that-Be.

I don't think so -- just a design decision in the case of bang methods
(they don't generally return self when there's no change, so they
can't be chained). It's a little tricky with hashes, because the
standard currency, so to speak, of a lot of Enumerable methods is the
array. So if you do, for example, find, select, reject, map, etc. on
a hash, you get back an array.


David
 
F

Florian Groß

Andrew said:
Something I often find useful is a version of Hash.delete which returns the
hash rather than the deleted item. Useful for terse chained code.

You want to do hash.delete("foo").delete("bar").delete("qux").

Why don't you do this?

%w(foo bar qux).each do |key|
hash.delete(key)
end

New keys can easily be added and is not repetitive at all.

Perhaps you could instead change your proposal to make .delete() accept
multiple keys?
 
D

David A. Black

--927295978-849992707-1114270237=:10750
Content-Type: MULTIPART/MIXED; BOUNDARY="927295978-849992707-1114270237=:10750"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

--927295978-849992707-1114270237=:10750
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

You want to do hash.delete("foo").delete("bar").delete("qux").

Why don't you do this?

%w(foo bar qux).each do |key|
hash.delete(key)
end

New keys can easily be added and is not repetitive at all.

Perhaps you could instead change your proposal to make .delete() accept= =20
multiple keys?

Or a delete_at for hash (similar to the array one).


David

--=20
David A. Black
(e-mail address removed)
--927295978-849992707-1114270237=:10750--
--927295978-849992707-1114270237=:10750--
 
A

Andrew Walrond

You want to do hash.delete("foo").delete("bar").delete("qux").

Yes, but more usually just an assignment:

attribs = {'version'=>version}.update(@versions[version]).remove('files')
Why don't you do this?

%w(foo bar qux).each do |key|
hash.delete(key)
end

Because small code is often beautiful code, and I never use three lines to do
a one line job ;)
New keys can easily be added and is not repetitive at all.

Perhaps you could instead change your proposal to make .delete() accept
multiple keys?

That would definately be a good, backwardly compatible setp.

Andrew
 
N

nobu.nokada

Hi,

At Sun, 24 Apr 2005 02:04:00 +0900,
Andrew Walrond wrote in [ruby-talk:139530]:
That would definately be a good, backwardly compatible setp.

What will be returned?
 
N

nobu.nokada

Hi,

At Sun, 24 Apr 2005 11:05:27 +0900,
Florian Groß wrote in [ruby-talk:139571]:
An Array of the deleted values?

It's not compatible obviously. Another method Hash#remove (and
Array#remove also?) sounds better, I guess.
 
A

Andrew Walrond

It's not compatible obviously. Another method Hash#remove (and
Array#remove also?) sounds better, I guess.

delete could return a value, or array of values, depending on whether the
number of arguments is 1 or >1, which would be compatible.

But #remove would be nicer :)

Andrew Walrond
 
F

Florian Groß

It's not compatible obviously. Another method Hash#remove (and
Array#remove also?) sounds better, I guess.

Oh, I don't think so. There is other methods which also return an Array
or one value depending on their arguments. Array#last comes to mind.

It might still be better to have another method for this, though. I'm
not sure.
 
N

nobu.nokada

Hi,

At Mon, 25 Apr 2005 04:44:32 +0900,
Florian Groß wrote in [ruby-talk:139655]:
Oh, I don't think so. There is other methods which also return an Array
or one value depending on their arguments. Array#last comes to mind.

It differs from this.

array.last => obj or nil
array.last(n) => an_array

Array#last doesn't allow arbitrary number arguments, but just 0
or 1.

Consider:

array = [...]
deleted = hash.delete(*array)

`deleted' will be an element if array's size equals 1, or an
array otherwise. It sounds like a pitfall.
 
D

Daniel Amelang

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.

Dan
 
N

nobu.nokada

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, 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, 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);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,171
Messages
2,570,935
Members
47,472
Latest member
KarissaBor

Latest Threads

Top