A use case for an ordered hash

J

James Edward Gray II

Coming to Ruby from JavaScript (among other paths) I personally
dislike
Ruby's two-character Hash delimiter. It's been brought up before,
and I
know that the colon makes it harder to parse with symbols, but I wish
we had a one-char (preferably without shift on an English keyboard
layout) hash delimiter.

hash = [ foo:bar, whee~la, dunno|what, delimiter=wouldbe ]

Ask and you shall receive:

$ ruby_yarv -ve 'p({foo: "bar", baz: "bar"})'
ruby 2.0.0 (Base: Ruby 1.9.0 2006-04-08) [i686-darwin8.7.1]
YARVCore 0.4.1 Rev: 527 (2006-07-19) [opts: [direct threaded code]
[inline method cache] ]
{:foo=>"bar", :baz=>"bar"}

James Edward Gray II
 
H

Hal Fulton

Phrogz said:
Coming to Ruby from JavaScript (among other paths) I personally dislike
Ruby's two-character Hash delimiter. It's been brought up before, and I
know that the colon makes it harder to parse with symbols, but I wish
we had a one-char (preferably without shift on an English keyboard
layout) hash delimiter.

hash = [ foo:bar, whee~la, dunno|what, delimiter=wouldbe ]

Heck, even two no-shift chars would be a nice improvement:

hash = [ foo--bar, jim..jam ]

...but now I'm hijacking the thread.

Hijack away, it's been discussed to death for years.

I agree I'd prefer a "nicer" notation than we have, but the
one we have is still better than nothing.

I'd like the single colon personally; or even the double colon,
but that already has a meaning.

As for symbols, I'd just require a space (which seems reasonable
to me):

hash = {123 : :abc, 234 : :def}

or even

hash = {123: :abc, 234: :def}

It looks a little funny, but at least it doesn't have commas
intruding between the key and value.


Hal
 
H

Hal Fulton

William said:
We already do; it's just that the pompous popinjays refuse to use it.

{ :gee, 'whiz', :gosh, 'darn' }
==>{:gee=>"whiz", :gosh=>"darn"}

Is that the way the syntax goes?


Hal
 
A

Austin Ziegler

We already do; it's just that the pompous popinjays refuse to use it.

Is it refusal, or a desire to do what is clearest and most compatible?

I'd personally love to see the 1.9 foo: bar format come to 1.8, but
wishes aren't horses.

-austin
 
T

Trans

my alib has an orderedhash which maintains insertion order.

fyi.

He he. Facets has one too called Dictionary (but also aliased as
OrderedHash) But I think Ruby would be better off for have the real
deal written in C.

T.
 
A

ara.t.howard

He he. Facets has one too called Dictionary (but also aliased as
OrderedHash) But I think Ruby would be better off for have the real
deal written in C.

indeed.

-a
 
P

Phrogz

William said:
{ :gee, 'whiz', :gosh, 'darn' }
==>{:gee=>"whiz", :gosh=>"darn"}

Is that 1.9? I get:

C:\>ruby -ve "p { :gee, 'whiz', :gosh, 'darn' }"
ruby 1.8.4 (2006-04-14) [i386-mswin32]
-e:1: syntax error
p { :gee, 'whiz', :gosh, 'darn' }
^
-e:1: syntax error
p { :gee, 'whiz', :gosh, 'darn' }
^
 
P

Phrogz

James said:
$ ruby_yarv -ve 'p({foo: "bar", baz: "bar"})'
ruby 2.0.0 (Base: Ruby 1.9.0 2006-04-08) [i686-darwin8.7.1]
YARVCore 0.4.1 Rev: 527 (2006-07-19) [opts: [direct threaded code]
[inline method cache] ]
{:foo=>"bar", :baz=>"bar"}

Yeehaw! Thanks, powers-that-be! :)

Whoa! And auto-symbolizing keys, too? Very, very exciting.
 
P

Phrogz

Phrogz said:
Is that 1.9? I get:

Bah...don't mind me. I'm an idiot. Of course that works in 1.8.4 if you
use parens around the hash instead of letting it think it's a block.
 
H

Hal Fulton

Phrogz said:
James said:
$ ruby_yarv -ve 'p({foo: "bar", baz: "bar"})'
ruby 2.0.0 (Base: Ruby 1.9.0 2006-04-08) [i686-darwin8.7.1]
YARVCore 0.4.1 Rev: 527 (2006-07-19) [opts: [direct threaded code]
[inline method cache] ]
{:foo=>"bar", :baz=>"bar"}


Yeehaw! Thanks, powers-that-be! :)

Whoa! And auto-symbolizing keys, too? Very, very exciting.

It's pretty cool. The only catch is that your keys have
to be symbols for this coolness to work, right?


Hal
 
J

James Edward Gray II

Phrogz said:
James said:
$ ruby_yarv -ve 'p({foo: "bar", baz: "bar"})'
ruby 2.0.0 (Base: Ruby 1.9.0 2006-04-08) [i686-darwin8.7.1]
YARVCore 0.4.1 Rev: 527 (2006-07-19) [opts: [direct threaded code]
[inline method cache] ]
{:foo=>"bar", :baz=>"bar"}
Yeehaw! Thanks, powers-that-be! :)
Whoa! And auto-symbolizing keys, too? Very, very exciting.

It's pretty cool. The only catch is that your keys have
to be symbols for this coolness to work, right?

Correct. It's just and alternate symbol Hash key syntax.

James Edward Gray II
 
S

Sven Suska

Hello to all,
Yeehaw! Thanks, powers-that-be! :)
Whoa! And auto-symbolizing keys, too? Very, very exciting.

Yes, I think this is useful
and I fully suport it.

And I am 100% in favour of a order-preserving hash in the Ruby core.

And actually, I would prefer the behavior of Hash to be *replaced*
by the behavior of "HistoryHash".
Enough people do want this feature,
a history-preserving hash does not cost much more (in terms of memory
and execution time)
and it is consistent with tho old hash behavior.
So, for the sake of simplicity, no new class, let's just replace
Hash by a "better" Hash.

This brings me to a point that I is very important to me:

Ruby crystallization.

I believe it is worth not only to add new useful syntax or features,
but also to strip less useful syntax/features.
Perhaps my plea comes a little bit late, now that Ruby already is
among the Tiobe top 20, but I think it is still worth some effort
to bring out the Ruby crystal more clearly.

[For a horror example, think of
the semantics of the switch statement in C.
- Historically (from the assembler point of view) it was logical
to have the option of omitting the break statement after a case,
but later it became a pain for generations of programmers.]

I am glad that some efforts towards "crystallization"
have already been happening:
For example, the use of parallel assignment is permitted in more places.

So as an example of what I think of in terms of syntax unification,
consider this:
Arrays are already very much integrated in the core syntax of Ruby,
but what about Hashes?
Given
def foo(a, b, c)
it is possible to call
array=[1,2,3]; foo(*array) # of course...
but why not
hash={:a=>1,:b=>2,:c=>3}; foo(**hash) # take any new symbol instead of
**

I mean, can we have a Hash-splat operator?
Extend the notion of parallel assignment in
such a way that it not only operates with position
but with name?
Then it would be much more straightforward to use
named paramters (as in Rails).

Is this useful or crazy?
name = "Paul"
city = "Paris"
**hash = name, city
=> {:name="Paul",:city="Paris"}

Of course, questions arise how to deal with unnamed values, ...
But my point is "Closer integration of Hashes in the language".

(And actually, order-preserving hashes would be a more suitable to do
that.)


OK, I got a bit carried away by this example,
the bottom line I was trying to make is:
just as already: every paramerter-list can be an array
let every paramter-list be useable like a hash.

And furthermore: simplification of proc/lambda/method/block.

Of course while/repeat simplification.

So much for now

Sven
 
P

Pit Capitain

Sven said:
And I am 100% in favour of a order-preserving hash in the Ruby core.

And actually, I would prefer the behavior of Hash to be *replaced*
by the behavior of "HistoryHash".
Enough people do want this feature,
a history-preserving hash does not cost much more (in terms of memory
and execution time)
and it is consistent with tho old hash behavior.
So, for the sake of simplicity, no new class, let's just replace
Hash by a "better" Hash.

Sven, regarding the "HistoryHash" part of your post:

In order to replace the standard Hash with a "better" Hash, there should
be a clear agreement on its desired behaviour. I don't think this
agreement has been achieved yet. What should be the result of the
following code?

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end

Should it be

:eek:ne
:two

or

:two
:eek:ne

Why should it be the one or the other?

Regards,
Pit
 
B

benjohn

Pit captain:
*snip*
Sven, regarding the "HistoryHash" part of your post:

In order to replace the standard Hash with a "better" Hash, there should
be a clear agreement on its desired behaviour. I don't think this
agreement has been achieved yet. What should be the result of the
following code?

*snip*

I've not been following this post attentively...

My vote is against the proposal (to add semantics to Hash)- for what
it's worth :)

I am very much in favour of the Hash remaining a Hash. It's an extremely
useful datatype, and it's clear what it does and doesn't do.

Personally, I would very much like to see an ordered container in the
standard distribution. It would behave like the c++ stl's map or set. (I
think these are commonly implemented on top of a b-tree, but other data
types are possible, I believe.)

Containers like this let you:
* iterate through entities in an ordered way.
* have log(n) insert time
* have log(n) search time
* let you to query for a close match (not necessarily an exact hit).

A Hash that also remembers insertion order seems like it could be
useful, although I don't think I've needed one (while I have wanted the
container described above). I definitely don't think such an order
preserving hash should replace what is in the standard though. In the
same way I wouldn't want to add semantics to Array or Set.

The pragmatic approach (and what I imagine will happen) is that if
someone wants these, they should write them. If they're then widely
used, they should go in to the standard distribution.

Cheers,
Benjohn
 
M

Martin DeMello

And actually, I would prefer the behavior of Hash to be *replaced*
by the behavior of "HistoryHash".
Enough people do want this feature,
a history-preserving hash does not cost much more (in terms of memory
and execution time)
and it is consistent with tho old hash behavior.
So, for the sake of simplicity, no new class, let's just replace
Hash by a "better" Hash.

For the record, I am 100% against this. Even if the semantics of
insertion order preservation could be defined to everyone's
satisfaction, it most assuredly does have an extra cost, and forcing
people who just want a normal hash to pay that cost is infeasible.

martin
 
S

Sven Suska

Pit said:
In order to replace the standard Hash with a "better" Hash, there should
be a clear agreement on its desired behaviour. I don't think this
agreement has been achieved yet. What should be the result of the
following code?

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end

Should it be
:eek:ne
:two
or
:two
:eek:ne
Why should it be the one or the other?

Hi Pit,

the first is the semantics of replacement,
the second the semantics of addition.

I think that replacement should be the normal semantics,
the second can then always be achieved by deleting the key
before assigning the new value.

The other way round, ie if addition were the default semantics,
we were forced to write hh[:eek:ne].replace( ), which does not
work for Fixnums.



But anyway, the recent posts induced some second thoughts in me
about having HistoryHash as the standard Hash:

Always having to keep the order can also be a drawback,
for instance:
Future core Ruby support for parallel execution could be such
that it allows different threads to add keys to one hash
and leave it undefined in which order the additions were made.

So, I am inclined to say:
Yes, OK, OK, the "good old" Hash is the cleaner solution.

Although the HistoryHash would have fitted better to my idea of
"parameter-list as Hash". But perhaps that really was too crazy.



Anyway, the performance cost of a HistoryHash would be
the cost of keeping an extra list of the entries,
this means (only) a pointer per entry.


Regards

Sven
 
A

ara.t.howard

Sven, regarding the "HistoryHash" part of your post:

In order to replace the standard Hash with a "better" Hash, there should be a
clear agreement on its desired behaviour. I don't think this agreement has
been achieved yet. What should be the result of the following code?

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end

Should it be

:eek:ne
:two

or

:two
:eek:ne

Why should it be the one or the other?

harp:~ > cat a.rb
require 'rubygems'
require 'alib'
HistoryHash = Alib::OrderedHash

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end


harp:~ > ruby a.rb
:eek:ne
:two


because it's based on __insertion__ order. if one wants a new insertion use:

harp:~ > cat a.rb
require 'rubygems'
require 'alib'
HistoryHash = Alib::OrderedHash

hh = HistoryHash.new

hh[:eek:ne] = 1
hh[:two] = 2
hh[:three] = 3

hh.delete :two
hh[:two] = 2

hh.each do |k, v| p k end


harp:~ > ruby a.rb
:eek:ne
:three
:two


of course that's my 2 cts.

-a
 
M

Martin DeMello

HistoryHash = Alib::OrderedHash

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end

harp:~ > ruby a.rb
:eek:ne
:two

because it's based on __insertion__ order. if one wants a new insertion use:

That depends on what you see yourself as inserting, though. If you
take it as k=>v pairs, then you can see :eek:ne => 1 being replaced by
:eek:ne => 3, which has then been inserted *after* :two => 2.

martin
 
A

ara.t.howard

HistoryHash = Alib::OrderedHash

hh = HistoryHash.new
hh[:eek:ne] = 1
hh[:two] = 2
hh[:eek:ne] = 3
hh.each do |k, v| p k end

harp:~ > ruby a.rb
:eek:ne
:two

because it's based on __insertion__ order. if one wants a new insertion
use:

That depends on what you see yourself as inserting, though. If you
take it as k=>v pairs, then you can see :eek:ne => 1 being replaced by
:eek:ne => 3, which has then been inserted *after* :two => 2.

i'd say :eek:ne was updated or replaced, not inserted - sorry i wasn't clear.

it is arbitrary though...

-a
 

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
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top