[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Behaviour of Enumerables reject vs. select mixed into Hash

Alexander Presber

6/20/2007 7:22:00 PM

Hello everybody,

Could you please take a look at the result of the following statements:

irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
=> {"baz"=>"qux", "foo"=>"bar"}

irb(main):002:0> a.reject{|k,v| k=='foo' }
=> {"baz"=>"qux"}

irb(main):003:0> a.select{|k,v| k=='baz' }
=> [["baz", "qux"]]

The result of the reject statement is clearly sensible: the original
hash minus the element with the key 'foo'.
But what about select? Shouldn't it return the same hash (instead of
an array of key-value pairs)?

Thanks for any comments
Sincerely yours
Alexander Presber

50 Answers

Trans

6/20/2007 10:27:00 PM

0



On Jun 20, 3:21 pm, Alexander Presber <aljos...@weisshuhn.de> wrote:
> Hello everybody,
>
> Could you please take a look at the result of the following statements:
>
> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
> => {"baz"=>"qux", "foo"=>"bar"}
>
> irb(main):002:0> a.reject{|k,v| k=='foo' }
> => {"baz"=>"qux"}
>
> irb(main):003:0> a.select{|k,v| k=='baz' }
> => [["baz", "qux"]]
>
> The result of the reject statement is clearly sensible: the original
> hash minus the element with the key 'foo'.
> But what about select? Shouldn't it return the same hash (instead of
> an array of key-value pairs)?

I have to concur. I've never liked that. You'd think there'd be some
way to have Enumerable act in accordance with the class it is
effecting, rather then dropping to the "LCD" --an array.

T.


Robert Dober

6/21/2007 8:17:00 AM

0

On 6/21/07, Trans <transfire@gmail.com> wrote:
>
>
> On Jun 20, 3:21 pm, Alexander Presber <aljos...@weisshuhn.de> wrote:
> > Hello everybody,
> >
> > Could you please take a look at the result of the following statements:
> >
> > irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
> > => {"baz"=>"qux", "foo"=>"bar"}
> >
> > irb(main):002:0> a.reject{|k,v| k=='foo' }
> > => {"baz"=>"qux"}
> >
> > irb(main):003:0> a.select{|k,v| k=='baz' }
> > => [["baz", "qux"]]
> >
> > The result of the reject statement is clearly sensible: the original
> > hash minus the element with the key 'foo'.
> > But what about select? Shouldn't it return the same hash (instead of
> > an array of key-value pairs)?
>
> I have to concur. I've never liked that. You'd think there'd be some
> way to have Enumerable act in accordance with the class it is
> effecting, rather then dropping to the "LCD" --an array.

So do I, Do you happen to have some remedies for this in Facets?
I am trying to unify such things in my library, but I am not building
something even close to Factes[1], however if you are interested in
some peaces let me know.

Cheers
Robert

[1] because than I would just use Facets which is really great, but I
want a small library and I want magic dot notation, which are liked by
few only :(

R.
>
> T.
>
>
>


--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Alexander Presber

6/21/2007 9:06:00 AM

0

>> Hello everybody,
>>
>> Could you please take a look at the result of the following
>> statements:
>>
>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>> => {"baz"=>"qux", "foo"=>"bar"}
>>
>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>> => {"baz"=>"qux"}
>>
>> irb(main):003:0> a.select{|k,v| k=='baz' }
>> => [["baz", "qux"]]
>>
>> The result of the reject statement is clearly sensible: the original
>> hash minus the element with the key 'foo'.
>> But what about select? Shouldn't it return the same hash (instead of
>> an array of key-value pairs)?
>
> I have to concur. I've never liked that. You'd think there'd be some
> way to have Enumerable act in accordance with the class it is
> effecting, rather then dropping to the "LCD" --an array.

Absolutely.
But the most baffling to me is, that it does not even act
_consistently_, see my original examples.
From a logical point of view (or at least the principle of least
surprise), selecting some elements should be identical
to dropping the others.

Is there anybody who can explain, why it had to be implemented that way?
Is there any chance of that getting changed in the future?

Sincerely yours,
Alexander Presber

Robert Klemme

6/21/2007 9:20:00 AM

0

On 21.06.2007 11:06, Alexander Presber wrote:
>>> Hello everybody,
>>>
>>> Could you please take a look at the result of the following statements:
>>>
>>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>>> => {"baz"=>"qux", "foo"=>"bar"}
>>>
>>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>>> => {"baz"=>"qux"}
>>>
>>> irb(main):003:0> a.select{|k,v| k=='baz' }
>>> => [["baz", "qux"]]
>>>
>>> The result of the reject statement is clearly sensible: the original
>>> hash minus the element with the key 'foo'.
>>> But what about select? Shouldn't it return the same hash (instead of
>>> an array of key-value pairs)?
>>
>> I have to concur. I've never liked that. You'd think there'd be some
>> way to have Enumerable act in accordance with the class it is
>> effecting, rather then dropping to the "LCD" --an array.
>
> Absolutely.
> But the most baffling to me is, that it does not even act
> _consistently_, see my original examples.
> From a logical point of view (or at least the principle of least
> surprise), selecting some elements should be identical
> to dropping the others.
>
> Is there anybody who can explain, why it had to be implemented that way?
> Is there any chance of that getting changed in the future?

The #select in Enumerable can only return a "general" type - in this
case an Array. Making Enumerable depend on the class is not a good idea
IMHO. Maybe an implementation using #dup would help though...

Of course Hash#select could act differently but that a) might hurt duck
typing and b) could break existing code. Note that you can always do
something like

selected = a_hash.dup.delete_if {|k,v| ... }

Kind regards

robert

Alexander Presber

6/21/2007 9:34:00 AM

0

> On 21.06.2007 11:06, Alexander Presber wrote:
>>>> Hello everybody,
>>>>
>>>> Could you please take a look at the result of the following
>>>> statements:
>>>>
>>>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>>>> => {"baz"=>"qux", "foo"=>"bar"}
>>>>
>>>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>>>> => {"baz"=>"qux"}
>>>>
>>>> irb(main):003:0> a.select{|k,v| k=='baz' }
>>>> => [["baz", "qux"]]
>>>>
>>>> The result of the reject statement is clearly sensible: the
>>>> original
>>>> hash minus the element with the key 'foo'.
>>>> But what about select? Shouldn't it return the same hash
>>>> (instead of
>>>> an array of key-value pairs)?
>>>
>>> I have to concur. I've never liked that. You'd think there'd be some
>>> way to have Enumerable act in accordance with the class it is
>>> effecting, rather then dropping to the "LCD" --an array.
>> Absolutely.
>> But the most baffling to me is, that it does not even act
>> _consistently_, see my original examples.
>> From a logical point of view (or at least the principle of least
>> surprise), selecting some elements should be identical
>> to dropping the others.
>> Is there anybody who can explain, why it had to be implemented
>> that way?
>> Is there any chance of that getting changed in the future?
>
> The #select in Enumerable can only return a "general" type - in
> this case an Array. Making Enumerable depend on the class is not a
> good idea IMHO. Maybe an implementation using #dup would help
> though...
>
> Of course Hash#select could act differently but that a) might hurt
> duck typing and b) could break existing code. Note that you can
> always do something like
>
> selected = a_hash.dup.delete_if {|k,v| ... }
>
> Kind regards
>
> robert
>
>


Alexander Presber

6/21/2007 9:58:00 AM

0

Am 21.06.2007 um 11:20 schrieb Robert Klemme:

> On 21.06.2007 11:06, Alexander Presber wrote:
>>>> Hello everybody,
>>>>
>>>> Could you please take a look at the result of the following
>>>> statements:
>>>>
>>>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>>>> => {"baz"=>"qux", "foo"=>"bar"}
>>>>
>>>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>>>> => {"baz"=>"qux"}
>>>>
>>>> irb(main):003:0> a.select{|k,v| k=='baz' }
>>>> => [["baz", "qux"]]
>>>>
>>>> The result of the reject statement is clearly sensible: the
>>>> original
>>>> hash minus the element with the key 'foo'.
>>>> But what about select? Shouldn't it return the same hash
>>>> (instead of
>>>> an array of key-value pairs)?
>>>
>>> I have to concur. I've never liked that. You'd think there'd be some
>>> way to have Enumerable act in accordance with the class it is
>>> effecting, rather then dropping to the "LCD" --an array.
>> Absolutely.
>> But the most baffling to me is, that it does not even act
>> _consistently_, see my original examples.
>> From a logical point of view (or at least the principle of least
>> surprise), selecting some elements should be identical
>> to dropping the others.
>> Is there anybody who can explain, why it had to be implemented
>> that way?
>> Is there any chance of that getting changed in the future?

Sorry for the empty post, to much clicking without thinking.

> The #select in Enumerable can only return a "general" type - in
> this case an Array.

I see, but isn't that restating the problem?

Even though "Programming Ruby" states: [snip] Enumerable#reject:
returns an array for all elements of enumObj for which block is false
(see also Enumerable#find_all) [/snip],
reject _respects_ the class it acts upon in returning a _hash_, not
an array.
Not so select.
Do you see the inconsistency?

> Making Enumerable depend on the class is not a good idea IMHO.
> Maybe an implementation using #dup would help though...

Could you please elaborate into that? I do not understand.

> Of course Hash#select could act differently but that a) might hurt
> duck typing

I fail to see why, could you give an example?

> and b) could break existing code.

That is true.

> Note that you can always do something like
>
> selected = a_hash.dup.delete_if {|k,v| ... }

Not quite. hsh.dup.delete_if is equivalent to hsh.reject, and reject
works just fine.
And of course I could always use Hash#reject (or hash#delete_if for
that matter) instead of Hash#select by just negating the block.

But the problem I see lies within the inconsistent return type of
Hash#select.

I could imagine if one wanted to change that behaviour, the "mixee"
class would have to provide certain capabilities to be able to get
Enumerable#select, much like objects have to provide an
implementation of <=> to get min and max.

> Kind regards
>
> robert

Yours truly,
Alexander

dblack

6/21/2007 10:08:00 AM

0

Robert Dober

6/21/2007 10:26:00 AM

0

On 6/21/07, dblack@wobblini.net <dblack@wobblini.net> wrote:
> Hi --
>
>
> >
> > I fail to see why, could you give an example?
>
> Anything that depends on the output having array-like behaviors:
>
> selected = enum.select(&select_block).flatten
That is ok, but we are complaining about

hash.select

Please note that the notion of Enumerable#select is erroneous in this
thread, sorry for being blunt Alexander but you were talking about
Hash#select the whole time.

I think this would be a picture case for breaking backwards
compatibility in Ruby2.0
Hash#select --> returning a Hash
and we would live in a better world ;)
unless you do not like it of course.

In other words it might be interesting to know if people like this
behavior without thinking about backwards compatibility or
implementation details.

Robert
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Alexander Presber

6/21/2007 10:36:00 AM

0


Am 21.06.2007 um 12:08 schrieb dblack@wobblini.net:

> Hi --
>
> On Thu, 21 Jun 2007, Alexander Presber wrote:
>
>> Am 21.06.2007 um 11:20 schrieb Robert Klemme:
>>
>>> On 21.06.2007 11:06, Alexander Presber wrote:
>>>>>> Hello everybody,
>>>>>> Could you please take a look at the result of the following
>>>>>> statements:
>>>>>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>>>>>> => {"baz"=>"qux", "foo"=>"bar"}
>>>>>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>>>>>> => {"baz"=>"qux"}
>>>>>> irb(main):003:0> a.select{|k,v| k=='baz' }
>>>>>> => [["baz", "qux"]]
>>>>>> The result of the reject statement is clearly sensible: the
>>>>>> original
>>>>>> hash minus the element with the key 'foo'.
>>>>>> But what about select? Shouldn't it return the same hash
>>>>>> (instead of
>>>>>> an array of key-value pairs)?
>>>>> I have to concur. I've never liked that. You'd think there'd be
>>>>> some
>>>>> way to have Enumerable act in accordance with the class it is
>>>>> effecting, rather then dropping to the "LCD" --an array.
>>>> Absolutely.
>>>> But the most baffling to me is, that it does not even act
>>>> _consistently_, see my original examples.
>>>> From a logical point of view (or at least the principle of least
>>>> surprise), selecting some elements should be identical
>>>> to dropping the others.
>>>> Is there anybody who can explain, why it had to be implemented
>>>> that way?
>>>> Is there any chance of that getting changed in the future?
>>
>> Sorry for the empty post, to much clicking without thinking.
>>
>>> The #select in Enumerable can only return a "general" type - in
>>> this case an Array.
>>
>> I see, but isn't that restating the problem?
>
> I think Robert's point is that nothing other than a general type makes
> sense. For example, if you select from an IO stream, you don't expect
> another IO stream.

Mhm. I thought IO#select is mixed in from the module Kernel, not
Enumerable. In this case, select can return whatever Kernel considers
appropriate.

I would like to think of the Enumerable mixin as an interface,
describing the general concept of "enumerability".
Rejecting and selecting certain elements of an enumerable set
certainly belong to this concept.

As I see it, a general recipe for selecting would be:
1) iterate (hence the need for enumerability) vfer the given set and
check the return value of the block for each element.
2) if true, keep, otherwise drop
3) return the resulting set, keeping all other properties (including
class) intact

Rejecting is just the opposite for 2).

>> Even though "Programming Ruby" states: [snip] Enumerable#reject:
>> returns an array for all elements of enumObj for which block is
>> false (see also Enumerable#find_all) [/snip],
>> reject _respects_ the class it acts upon in returning a _hash_,
>> not an array.
>> Not so select.
>> Do you see the inconsistency?
>
> Hash overrides Enumerable#reject. I'm not sure why, except possibly
> because of the relation between reject and delete_if.

>>> Making Enumerable depend on the class is not a good idea IMHO.
>>> Maybe an implementation using #dup would help though...
>>
>> Could you please elaborate into that? I do not understand.
>>
>>> Of course Hash#select could act differently but that a) might
>>> hurt duck typing
>>
>> I fail to see why, could you give an example?
>
> Anything that depends on the output having array-like behaviors:
>
> selected = enum.select(&select_block).flatten

I don't get what that has to do with duck typing.

The possibility to _flatten_ something requires it to be an Array-
duck, not an Enumerable-duck. That is why flatten is part of Array.
Nobody should expect the result of Enumerable#select to be flattenable.

If however there is a sensible way to make the Enumerable an Array
(in order to flatten it), you should go:

selected = enum.select(&select_block).to_a.flatten

(note the to_a)

Sincerely yours,
Alex

dblack

6/21/2007 10:37:00 AM

0