Hash with Integer key issue

Hi Im new to Ruby and am getting some unexpected results from a
dynamically created Hash. The following from irb is my attempt to
troubleshoot why its not working.

irb(main):028:0> x = Hash.new
=> {}

Dynamically build the hash, works.

irb(main):029:0> (1…49).each { |y| x[y] = y-2 }
=> 1…49

irb(main):030:0> x
=> {1=>-1, 2=>0, 3=>1, 4=>2, 5=>3, 6=>4, 7=>5, 8=>6, 9=>7, 10=>8, 11=>9,
12=>10, 13=>11, 14=>12, 15=>13, 16=>14, 17=>15, 18=>16, 19=>17, 20=>18,
21=>19, 22=>20, 23=>21, 24=>22, 25=>23, 26=>24, 27=>25, 28=>26, 29=>27,
30=>28, 31=>29, 32=>30, 33=>31, 34=>32, 35=>33, 36=>34, 37=>35, 38=>36,
39=>37, 40=>38, 41=>39, 42=>40, 43=>41, 44=>42, 45=>43, 46=>44, 47=>45,
48=>46, 49=>47}

Gives the result I expect

irb(main):031:0> x[46]
=> 44

Sort by the value…

irb(main):032:0> x = x.sort_by {|k,v| v}.reverse
=> [[49, 47], [48, 46], [47, 45], [46, 44], [45, 43], [44, 42], [43,
41], [42, 40], [41, 39], [40, 38], [39, 37], [38, 36], [37, 35], [36,
34], [35, 33], [34, 32], [33, 31], [32, 30], [31, 29], [30, 28], [29,
27], [28, 26], [27, 25], [26, 24], [25, 23], [24, 22], [23, 21], [22,
20], [21, 19], [20, 18], [19, 17], [18, 16], [17, 15], [16, 14], [15,
13], [14, 12], [13, 11], [12, 10], [11, 9], [10, 8], [9, 7], [8, 6], [7,
5], [6, 4], [5, 3], [4, 2], [3, 1], [2, 0], [1, -1]]

And here is the problem! Now that I am looking at it here, it’s

obvious that the sort is flattening the Hash… Is there a way to
restore it to a Hash, or a better way to achieve the sort?

irb(main):033:0> x[46]
=> [3, 1]

I’m sure I’m over looking something simple. Please help, I need the
sorted Hash to give me the appropriate value for the Integer key…

Sorting the hash creates a nested “array” which you assigned back to x.
If you type check x, it is now an array.

S

Excerpts from Wayne Simmerson’s message of 2012-10-19 19:12:16 -0400:

=> 44

And here is the problem! Now that I am looking at it here, it’s

obvious that the sort is flattening the Hash… Is there a way to
restore it to a Hash, or a better way to achieve the sort?

irb(main):033:0> x[46]
=> [3, 1]

I’m sure I’m over looking something simple. Please help, I need the
sorted Hash to give me the appropriate value for the Integer key…


“Truth or die.”

Steven Hum
5 - 28 Gilmour St
Ottawa, ON K2P 0N3
email [email protected]
tel 613.237.9058

And here is the problem! Now that I am looking at it here, it’s

obvious that the sort is flattening the Hash… Is there a way to
restore it to a Hash, or a better way to achieve the sort?

irb(main):033:0> x[46]
=> [3, 1]

Does this help?

arr = [[49, 47], [48, 46], [47, 45], [46, 44], [45, 43], [44, 42]]
p Hash[*arr.flatten]

#> {49=>47, 48=>46, 47=>45, 46=>44, 45=>43, 44=>42}

Harry

Note that “Hash[arr]” works too!

Wayne Simmerson wrote in post #1080492:

And here is the problem! Now that I am looking at it here, it’s

obvious that the sort is flattening the Hash… Is there a way to
restore it to a Hash, or a better way to achieve the sort?

Hi,

the standard “sort” method always returns an array of the objects coming
from the “each” method. It does not return the original data
structure. In other words: Whatever you sort, you always get back a
sorted array.

This usually works fine, because most of the time you just want to have
the elements in a specific order. If you actually need to retain the
original data structure, you have to rebuild the object from the array
(as Harry and Arlen demonstrated).

In this case, however, I think it’s in fact a misunderstanding of
hashes. The idea of a hash is to map keys to values. It doesn’t have an
inner order(*). So you might want to rethink if this is the right data
structure for your purpose. Maybe you just want an array?

(*)
In newer versions of Ruby, hashes actually do have an inner order
(probably to be less surprising to newbies). So they’re really a hybrid
of a hash an an array. However, I still wouldn’t use them as an ordered
sequence of elements but to map keys to values.

And here is the problem! Now that I am looking at it here, it’s

obvious that the sort is flattening the Hash… Is there a way to
restore it to a Hash, or a better way to achieve the sort?

Or you could generate it like this. It depends on what you are trying to
do.

x = Hash.new
49.downto(1).each { |y| x[y] = y-2 }
p x

Harry

Or you could generate it like this. It depends on what you are trying to do.

x = Hash.new
49.downto(1).each { |y| x[y] = y-2 }
p x

Actually, you do not need the #each.

x = Hash.new
49.downto(1) {|y| x[y] = y-2}
p x

Harry

Am 20.10.2012 14:46, schrieb Wayne Simmerson:

Thanks for all the replies, I actually figured it out about 2 minutes
after I posted the question. I was banging my head against my desk for
hours trying to figure out why it wasn’t working, completely oblivious
to the fact it had been converted to an Array. It took writing it out
here to notice the Hash had been Arrayed!

Is there no way to sort the hash and convert it back in the same
statement? Would be nice to do in a single line of code.

Arlen already told you!

new_hash = Hash[x.sort_by {|k,v| v }.reverse]

But I do not see the use case for a sorted hash,
it appears conceptually wrong to me. Have you read Jan’s post?

Thanks for all the replies, I actually figured it out about 2 minutes
after I posted the question. I was banging my head against my desk for
hours trying to figure out why it wasn’t working, completely oblivious
to the fact it had been converted to an Array. It took writing it out
here to notice the Hash had been Arrayed!

Is there no way to sort the hash and convert it back in the same
statement? Would be nice to do in a single line of code.

unknown wrote in post #1080532:

Am 20.10.2012 14:46, schrieb Wayne Simmerson:

Thanks for all the replies, I actually figured it out about 2 minutes
after I posted the question. I was banging my head against my desk for
hours trying to figure out why it wasn’t working, completely oblivious
to the fact it had been converted to an Array. It took writing it out
here to notice the Hash had been Arrayed!

Is there no way to sort the hash and convert it back in the same
statement? Would be nice to do in a single line of code.

Arlen already told you!

new_hash = Hash[x.sort_by {|k,v| v }.reverse]

But I do not see the use case for a sorted hash,
it appears conceptually wrong to me. Have you read Jan’s post?

If you believe that Hashes are unsorted, then why would you reverse an
array before converting it to a Hash? Or sort the array?

~/ruby_programs$ ruby --version
ruby 1.8.7 (2012-06-29 patchlevel 370) [i686-darwin10.8.0]

x = {
1 => 2,
0 => 1,
}

p x

result = x.sort_by {|k, v| v}
p Hash[result]

–output:–
{0=>1, 1=>2}
{0=>1, 1=>2}

Hashes are unsorted. However, in Ruby 1.9 (but not in 1.8) they are
ordered; that is, when you iterate over them, the elements are yielded
in the same order as they were inserted.

– Matma R.

Am 20.10.2012 18:23, schrieb 7stud --:

Arlen already told you!

new_hash = Hash[x.sort_by {|k,v| v }.reverse]

But I do not see the use case for a sorted hash,
it appears conceptually wrong to me. Have you read Jan’s post?

If you believe Hashes are unsorted, then why would you reverse an array
before converting it to a Hash?

Have you read the previous posts in this thread?
The code above was given in reply to the OP’s question, and it works
as expected in Ruby 1.9.3 because hashes are indeed sorted there.
(It probably should have been mentioned that it fails in Ruby 1.8).

What I did want to point out is that I do not see why a hash is used
here, when order seems to be crucial to the OP’s purposes.

unknown wrote in post #1080538:

Have you read the previous posts in this thread?
The code above was given in reply to the OP’s question, and it works
as expected in Ruby 1.9.3 because hashes are indeed sorted there.

FWIW, they are not sorted, but they do retain the original insertion
order.

If you want a collection which is always sorted, then you need something
like a heap.

Why does every single thread in this forum turn into an endless expert
discussion about terms or technical details?

All the OP wanted to know is how to sort a hash. Harry showed him, and I
made a remark that a hash might not be the best way to represent a
sequence of numbers, even though it can be used that way in newer
versions of Ruby. Can’t we just leave it at that?

If somebody used a wrong word somewhere, that’s a pity. But who cares? I
think it’s clear to everybody what we mean.

Am 21.10.2012 15:33, schrieb Jan E.:

Why does every single thread in this forum turn into an endless expert
discussion about terms or technical details?

+1

Am 21.10.2012 14:33, schrieb Brian C.:

unknown wrote in post #1080538:

Have you read the previous posts in this thread?
The code above was given in reply to the OP’s question, and it works
as expected in Ruby 1.9.3 because hashes are indeed sorted there.

FWIW, they are not sorted, but they do retain the original insertion
order.

That’s hair splitting…
one can say they are sorted by insertion order,
and in the considered example only that matters.

If you want a collection which is always sorted, then you need something
like a heap.

Sigh… that’s exactly my point, All I wanted to convey to the OP
is that he most probably should not use a hash here.

Maybe the problem here is that I’m not a native speaker.

Am 22.10.2012 01:19, schrieb Henry M.:

Teaching people technical details will help them figure things out for
themselves.

Henry

But dissecting each word of every fast answer to some simple question
and dragging on and on might turn off contributors from this list.

On 22/10/2012, at 2:33 AM, “Jan E.” [email protected] wrote:

Why does every single thread in this forum turn into an endless expert
discussion about terms or technical details?

Because if you misunderstand the terms or technical details then you
won’t understand why things don’t work as you expect, eg. why sorting a
hash returns an array,

or calling a hash sorted vs ordered will raise more questions than it
answers.

Teaching people technical details will help them figure things out for
themselves.

Henry