Hash#select returns an array but Hash#reject returns a hash...

  • Thread starter Srijayanth Sridhar
  • Start date
S

Srijayanth Sridhar

Hello,

Is there any design reason for select returning an array and reject
returning a hash? Why this disparity?

Thank you,

Jayanth
 
D

Dave Bass

Srijayanth said:
Is there any design reason for select returning an array and reject
returning a hash? Why this disparity?

In module Enumerable, both select and reject return an array (according
to the Pickaxe).
 
S

Srijayanth Sridhar

irb(main):001:0> a=Hash.new
=> {}
irb(main):002:0> a[1]=2
=> 2
irb(main):003:0> a[2]=2
=> 2
irb(main):004:0> a[3]=4
=> 4
irb(main):005:0> a.select { |key,value| value > 2 }
=> [[3, 4]]
irb(main):006:0> a.reject { |key,value| value <= 2 }
=> {3=>4}
 
P

Peña, Botp

From: Srijayanth Sridhar [mailto:[email protected]]=20
# irb(main):001:0> a=3DHash.new
# =3D> {}
# irb(main):002:0> a[1]=3D2
# =3D> 2
# irb(main):003:0> a[2]=3D2
# =3D> 2
# irb(main):004:0> a[3]=3D4
# =3D> 4
# irb(main):005:0> a.select { |key,value| value > 2 }
# =3D> [[3, 4]]
# irb(main):006:0> a.reject { |key,value| value <=3D 2 }
# =3D> {3=3D>4}

that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=3D> "1.9.0"
irb(main):016:0> a=3DHash.new
=3D> {}
irb(main):017:0> a[1]=3D2
=3D> 2
irb(main):018:0> a[2]=3D2
=3D> 2
irb(main):019:0> a[3]=3D4
=3D> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=3D> {3=3D>4}
irb(main):021:0> a.reject { |key,value| value <=3D 2 }
=3D> {3=3D>4}

kind regards -botp
 
S

Srijayanth Sridhar

that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=> "1.9.0"
irb(main):016:0> a=Hash.new
=> {}
irb(main):017:0> a[1]=2
=> 2
irb(main):018:0> a[2]=2
=> 2
irb(main):019:0> a[3]=4
=> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=> {3=>4}
irb(main):021:0> a.reject { |key,value| value <= 2 }
=> {3=>4}

kind regards -botp

Thanks.

So this is just some sort of artefact/legacy code that never got changed?

Jayanth
 
D

David A. Black

Hi --

that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=> "1.9.0"
irb(main):016:0> a=Hash.new
=> {}
irb(main):017:0> a[1]=2
=> 2
irb(main):018:0> a[2]=2
=> 2
irb(main):019:0> a[3]=4
=> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=> {3=>4}
irb(main):021:0> a.reject { |key,value| value <= 2 }
=> {3=>4}

kind regards -botp

Thanks.

So this is just some sort of artefact/legacy code that never got changed?

You can actually make a case that select and reject are not exactly
symmetrical operations. Imagine a line of people:


Joe John Joan David Jim Jenny Jeff Matz

If I tell everyone in the line whose name does not begin with J to
step backwards (reject), the original line is smaller but it's still
the same line.

If I tell everyone whose name *does* begin with J to step forwards
(select), I've got a new line of J people.

"Same line" and "new line" don't necessarily map to "same object" and
"new object" in Ruby (since the post-reject hash is a different hash).
But it suggests that there's a difference, arguably, between select
and reject, in terms of the formal disturbance of the object, which in
turn makes it easier to understand why select would return objects in
a different container, while reject would leave the container in the
same form but just contain fewer things.

However, that doesn't mean it's bad for select to return a hash (which
it does, as was mentioned, as of 1.9). Just that the behavior of one
doesn't necessarily imply the behavior of the other.


David
 
S

Srijayanth Sridhar

Well put. Thanks again.

Jayanth

Hi --

that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=> "1.9.0"
irb(main):016:0> a=Hash.new
=> {}
irb(main):017:0> a[1]=2
=> 2
irb(main):018:0> a[2]=2
=> 2
irb(main):019:0> a[3]=4
=> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=> {3=>4}
irb(main):021:0> a.reject { |key,value| value <= 2 }
=> {3=>4}

kind regards -botp

Thanks.

So this is just some sort of artefact/legacy code that never got changed?

You can actually make a case that select and reject are not exactly
symmetrical operations. Imagine a line of people:


Joe John Joan David Jim Jenny Jeff Matz

If I tell everyone in the line whose name does not begin with J to
step backwards (reject), the original line is smaller but it's still
the same line.

If I tell everyone whose name *does* begin with J to step forwards
(select), I've got a new line of J people.

"Same line" and "new line" don't necessarily map to "same object" and
"new object" in Ruby (since the post-reject hash is a different hash).
But it suggests that there's a difference, arguably, between select
and reject, in terms of the formal disturbance of the object, which in
turn makes it easier to understand why select would return objects in
a different container, while reject would leave the container in the
same form but just contain fewer things.

However, that doesn't mean it's bad for select to return a hash (which
it does, as was mentioned, as of 1.9). Just that the behavior of one
doesn't necessarily imply the behavior of the other.


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
Advancing With Rails August 18-21 Edison, NJ
See http://www.rubypal.com for details and updates!
 
R

Rick DeNatale

[Note: parts of this message were removed to make it a legal post.]

Hi --


that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=> "1.9.0"
irb(main):016:0> a=Hash.new
=> {}
irb(main):017:0> a[1]=2
=> 2
irb(main):018:0> a[2]=2
=> 2
irb(main):019:0> a[3]=4
=> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=> {3=>4}
irb(main):021:0> a.reject { |key,value| value <= 2 }
=> {3=>4}

kind regards -botp
Thanks.

So this is just some sort of artefact/legacy code that never got changed?

You can actually make a case that select and reject are not exactly
symmetrical operations. Imagine a line of people:


Joe John Joan David Jim Jenny Jeff Matz

If I tell everyone in the line whose name does not begin with J to
step backwards (reject), the original line is smaller but it's still
the same line.

If I tell everyone whose name *does* begin with J to step forwards
(select), I've got a new line of J people.


I'm having a hard time making the connection between this analogy and the
methods.

In the first case one could say that we end up with two lines, the
'original' one and the line of rejects.

But first of all, x.reject leaves x alone so the original 'line' is
unchanged.

And why can't we see your select example exactly the same way, except with
the resultant line of 'selects' just closer to you.

"Same line" and "new line" don't necessarily map to "same object" and
"new object" in Ruby (since the post-reject hash is a different hash).
But it suggests that there's a difference, arguably, between select
and reject, in terms of the formal disturbance of the object, which in
turn makes it easier to understand why select would return objects in
a different container, while reject would leave the container in the
same form but just contain fewer things.


Except that both select and reject leave the original container the same.
The analogy might be more apt for reject! and select! (if the latter method
existed).

However, that doesn't mean it's bad for select to return a hash (which
it does, as was mentioned, as of 1.9). Just that the behavior of one
doesn't necessarily imply the behavior of the other.

The real change, it seems to me is that pre 1.9 the Ruby enumerator methods
had/have a preference for returning arrays rather than an instance of the
same class as the receiver, whereas 1.9 seems to be shifting to a preference
for returning an instance of the same class as the receiver where that makes
sense.
 
R

Robert Dober

I rather agree with Rick although David has a point too.

I therefore implemented hselect in Labrador (also kselect for keys and
vselect for values) all returning hashes. Parts of Labrador are
however highly experimental - and I intend to put some order into this
soon - therefore maybe you can find what you want in Facets which is a
highly tested and widely used framework or just borrow my code from
Labrador (BSD license).

HTH
Robert
 
S

Srijayanth Sridhar

I rather agree with Rick although David has a point too.

Yeah I agree with Rick as well or I wouldn't have asked the question
in the first place.

I therefore implemented hselect in Labrador (also kselect for keys and
vselect for values) all returning hashes. Parts of Labrador are
however highly experimental - and I intend to put some order into this
soon - therefore maybe you can find what you want in Facets which is a
highly tested and widely used framework or just borrow my code from
Labrador (BSD license).

I am just hacking up the classes as of now. Don't really intend to use
it anywhere.

Thank you all,

Jayanth
 
D

David A. Black

Hi --

Hi --


that will change in newer version of ruby, eg ruby1.9

irb(main):015:0> RUBY_VERSION
=> "1.9.0"
irb(main):016:0> a=Hash.new
=> {}
irb(main):017:0> a[1]=2
=> 2
irb(main):018:0> a[2]=2
=> 2
irb(main):019:0> a[3]=4
=> 4
irb(main):020:0> a.select { |key,value| value > 2 }
=> {3=>4}
irb(main):021:0> a.reject { |key,value| value <= 2 }
=> {3=>4}

kind regards -botp



Thanks.

So this is just some sort of artefact/legacy code that never got changed?

You can actually make a case that select and reject are not exactly
symmetrical operations. Imagine a line of people:


Joe John Joan David Jim Jenny Jeff Matz

If I tell everyone in the line whose name does not begin with J to
step backwards (reject), the original line is smaller but it's still
the same line.

If I tell everyone whose name *does* begin with J to step forwards
(select), I've got a new line of J people.


I'm having a hard time making the connection between this analogy and the
methods.

In the first case one could say that we end up with two lines, the
'original' one and the line of rejects.

But first of all, x.reject leaves x alone so the original 'line' is
unchanged.

And why can't we see your select example exactly the same way, except with
the resultant line of 'selects' just closer to you.

"Same line" and "new line" don't necessarily map to "same object" and
"new object" in Ruby (since the post-reject hash is a different hash).
But it suggests that there's a difference, arguably, between select
and reject, in terms of the formal disturbance of the object, which in
turn makes it easier to understand why select would return objects in
a different container, while reject would leave the container in the
same form but just contain fewer things.


Except that both select and reject leave the original container the same.
The analogy might be more apt for reject! and select! (if the latter method
existed).

It's specifically not about the exact objects, though. As I said,
"same line" in this model doesn't map to "same object". Rather, the
analogy has to do with types of structure or container.

Here's another, perhaps better analogy. You've got a shelf of books,
and you've got an empty box. You select some books from the shelf, and
put them in the box. Now you've got two structures: a shelf, and a
box.

If you were describing this operation, you'd say: "I selected some
books and put them in a box." The box of books is the result of the
operation, and is structurally different from the shelf. The point is
not that this is the only way it can be done, but that it *can* be
done this way. It's reasonable to select from a collection into
something that is not only a different collection, but a different
container type.

Now, if you do it reject-wise, you take the books you don't want and
throw them out the window. The box never enters into it. You're not
selecting, so you don't care about observing or measuring the books
that you're removing from the shelf.

And the shelf is still a shelf. There's no new type of container
involved, just the same type of container with different contents. The
reject operation does not imply the transfer of objects to a different
type of container. Also, the fact that Ruby can do this in terms of
duplicate objects, rather than in place, isn't relevant. The change or
conservation of the container type is what's important.

That's all I'm saying :)

And thereafter, to make sense of the new 1.9 behavior, all you have to
do is decide that instead of a box, you're putting the selected books
on another shelf.

Again, this has nothing to do with the identity of objects vs. copies.
It's about the proliferation (or not) of structures.
The real change, it seems to me is that pre 1.9 the Ruby enumerator methods
had/have a preference for returning arrays rather than an instance of the
same class as the receiver, whereas 1.9 seems to be shifting to a preference
for returning an instance of the same class as the receiver where that makes
sense.

Yes, that's clearly the change. I wasn't talking about the change,
though; I was suggesting a model for understanding why the pre-change
way might not have been as irrational as has sometimes been suggested.
It's all moot as of 1.9.


David
 
D

David A. Black

Hi --

Yeah I agree with Rick as well or I wouldn't have asked the question
in the first place.

Rick made some points about in-place changes vs. copies of Ruby
objects that I agree with, but that wasn't what my post was about :)
See my last one too.


David
 
Y

Yossef Mendelssohn

Now, if you do it reject-wise, you take the books you don't want and
throw them out the window. The box never enters into it. You're not
selecting, so you don't care about observing or measuring the books
that you're removing from the shelf.

Throw them out the window? You could hurt someone.

Let's not be so hasty, David. You could put those books in the box and
take them to a used-book store instead.
 
D

David A. Black

Hi --

Throw them out the window? You could hurt someone.

Let's not be so hasty, David. You could put those books in the box and
take them to a used-book store instead.

Yeah, I thought of that, but I figured the window would make the point
better :) Besides, used book stores have gotten so picky lately....


David
 
R

Rick DeNatale

[Note: parts of this message were removed to make it a legal post.]

Hi --



It's specifically not about the exact objects, though. As I said,
"same line" in this model doesn't map to "same object". Rather, the
analogy has to do with types of structure or container.

Here's another, perhaps better analogy. You've got a shelf of books,
and you've got an empty box. You select some books from the shelf, and
put them in the box. Now you've got two structures: a shelf, and a
box.

If you were describing this operation, you'd say: "I selected some
books and put them in a box." The box of books is the result of the
operation, and is structurally different from the shelf. The point is
not that this is the only way it can be done, but that it *can* be
done this way. It's reasonable to select from a collection into
something that is not only a different collection, but a different
container type.

Now, if you do it reject-wise, you take the books you don't want and
throw them out the window. The box never enters into it. You're not
selecting, so you don't care about observing or measuring the books
that you're removing from the shelf.

I still have the same problem with this analogy.

reject and select are not about the books. They don't move books from one
collection to another. They BOTH return a new collection which references
some of the objects referenced by the receiver.

select returns a collection of all of the objects in the receiver which meet
the criterion specified by the block, reject returns a collection of all the
objects in the receiver which DON'T meet the criterion specified by the
block.

The fact that Ruby <1.9 tends to return arrays rather than something of the
same species as the receiver on such methods was something I had to get used
to after my Smalltalk experience.

And, compatibility aside, I think that 1.9 is going in the right direction
here.
 
J

Jeremy McAnally

OK, then there's a box of books. You get another box. You move books
that you like from the first box to the second and ditch the others.
It's all the same and the minutia aren't really that important when
describing the general behavior.

I think the analogy works perfectly fine to get the point across about
the behavior of the method.

--Jeremy

I still have the same problem with this analogy.

reject and select are not about the books. They don't move books from one
collection to another. They BOTH return a new collection which references
some of the objects referenced by the receiver.

select returns a collection of all of the objects in the receiver which meet
the criterion specified by the block, reject returns a collection of all the
objects in the receiver which DON'T meet the criterion specified by the
block.

The fact that Ruby <1.9 tends to return arrays rather than something of the
same species as the receiver on such methods was something I had to get used
to after my Smalltalk experience.

And, compatibility aside, I think that 1.9 is going in the right direction
here.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/



--
http://jeremymcanally.com/
http://entp.com

Read my books:
Ruby in Practice (http://manning.com/mcanally/)
My free Ruby e-book (http://humblelittlerubybook.com/)

Or, my blogs:
http://mrneighborly.com
http://rubyinpractice.com
 
R

Robert Dober

Hi --



Rick made some points about in-place changes vs. copies of Ruby
objects that I agree with, but that wasn't what my post was about :)
See my last one too.
Actually I think the most important thing about this is, yes I know
you guessed and you disagree, consistency;)
But give me a chance, it can be consistent that whatever.reject.class
== whatever.class and
whatever.select.class == Array. That was exactly why I named my Hash
returning methods hselect, vselect and kselect.

However personally I would prefer a different kind of consistency on
this one, as some others would, I do not even daresay that we are a
majority ;).

Cheers
Robert
 
D

David A. Black

Hi --

Actually I think the most important thing about this is, yes I know
you guessed and you disagree, consistency;)
But give me a chance, it can be consistent that whatever.reject.class
== whatever.class and
whatever.select.class == Array. That was exactly why I named my Hash
returning methods hselect, vselect and kselect.

It depends on the class, though. Neither File#select nor File#reject
is going to be return a File object.


David
 
R

Robert Dober

It depends on the class, though. Neither File#select nor File#reject
is going to be return a File object.
OMG, but are these names really baring the same semantic meaning?
Robert
 
D

David A. Black

Hi --

OMG, but are these names really baring the same semantic meaning?

It's all Enumerable-based, ultimately, though some classes do
overrides.


David
 

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

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top