Array#to_h


#1

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?
Thanks!
-=r


#2

Hi –

On Sun, 1 Feb 2009, Roger P. wrote:

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?

Not if it’s not useful :slight_smile: It might be, though. It’s been talked about
a lot over the years. As I recall, part of the problem is the question
of what it would mean; for example, given this:

[“a”,“b”,“c”,“d”].to_h

is it

[“a” => “b”, “c” => “d”]

or

[0 => “a”, 1 => “b”, …]

?

David


David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!


#3

On Sat, Jan 31, 2009 at 8:49 PM, Roger P. removed_email_address@domain.invalid
wrote:

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?
Thanks!

A possible implementation:

class Array
def to_h
Hash[*self]
end
end
=> nil

[1,2,3,4].to_h
=> {1=>2, 3=>4}

Jesus.


#4

See here:
http://www.fivesevensix.com/posts/2005/05/20/array-to_h

…and here:
http://drawohara.com/post/70998078/ruby-array-to-hash-rocks

  • Josh

#5

2009/1/31 Joshua B. removed_email_address@domain.invalid

See here:
http://www.fivesevensix.com/posts/2005/05/20/array-to_h

…and here:
http://drawohara.com/post/70998078/ruby-array-to-hash-rocks

  • Josh

This question came up a while back where I helped someone with yet
another
interpretation:

http://blog.jcoglan.com/2008/09/08/enumerableto_hash-for-unix-style-flags-in-ruby-methods/

The core problem with Hash#to_a and any possible Enumerable#to_h is it’s
not
intuitively obvious what either should do, though Hash#to_a is possibly
slightly narrower in scope. There are any number of ways you might want
to
map one to the other, and that’s really what map() and inject() are for.


#6

On 31.01.2009 21:03, David A. Black wrote:

?
Or even raise an Exception because Array#to_h expects a nested structure
as is returned from various Hash methods (e.g. #select, #to_a - note,
this is about to change in new versions of Ruby).

Kind regards

robert


#7

On Sat, Jan 31, 2009 at 7:49 PM, Roger P. removed_email_address@domain.invalid
wrote:

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?
Thanks!
-=r

It’s useful in Ruby 1.8.x for those enumerable methods which return
arrays, e…g

irb --> {:a => 1, :b => 2, :c => 3}.select{|k, v| v % 2 == 1 }
==> [[:c, 3], [:a, 1]]

irb --> {:a => 1, :b => 2, :c => 3}.select{|k, v| v % 2 == 1 }.to_hash
==> {:a=>1, :c=>3}

whereas in Ruby 1.9.x, select (map, etc.) return hashes:

irb(main):001:0> {:a => 1, :b => 2, :c => 3}.select{|k, v| v % 2 == 1 }
=> {:a=>1, :c=>3}

The problem is, as other posters have noted, that there is no 1-1
mapping between an array and a hash. Having said that, I’ve found the
following to be handy (mainly in restoring a hash after the 1.8.x
transformation to an array):

module ToHash
if RUBY_VERSION =~ /^1.9/
def to_hash
Hash[flatten(1)]
end
else
def to_hash
Hash[
(inject([]) {|arr, i| i.kind_of?(Array) ? arr.push(*i) :
arr.push(i) })]
end
end
end

used like this:

b = [
[:a, [[1, 2], [3, 4]]],
[:b, 3],
[:c, { :d => 4 }],
]

p b.extend(ToHash).to_hash

Regards,
Sean


#8

2009/2/6 Trans removed_email_address@domain.invalid:

various options might be addressed. So I came up with this:

Converts an associative array into a hash.

By using a +mode+ we can offer a variety of common means of
conversion.

Of course, we could just make them all separate methods, ie. #to_h,
#to_h_array, #to_h_splat, #to_h_flat. Maybe that is better?

This is generally considered better practice over switching behavior
of a method with an argument. Just think about the length of the
method which increases in with the number of different algorithms
(modes). I prefer short methods.

Alternatively make the algorithm detection automatic. Even in that
case I had to_h only do the detection and then delegate to any one of
a number of to_h_ methods. The rule I follow is to create
methods and classes to do one thing good.

But then
that seems a bit more limiting, less dynamic, less open for new modes
or multiple labels for a single mode, and the method names look funny

Not at all: you can simply add another method.

This is how I’d approach it:

module Enumerable
def to_h
pairs = arr = 0

each do |e|
  if Array === e
    if e.size <= 2
      pairs += 1
    else
      arr += 1
    end
  end
end

case
when pairs == size
  to_h_pairs
when arr > 0
  to_h_multi
else
  to_h_flat
end

end

def to_h_pairs
inject({}) {|ha,(k,v)| ha[k]=v; ha}
end

def to_h_multi
inject({}) {|ha,ar| ha[ar.first] = ar[1…-1]; ha}
end

def to_h_flat
each_slice(2).inject({}) {|ha,(k,v)| ha[k]=v; ha}
end
end

[
[1,2,3,4,5],
[[1,2],[3,4],[5,6]],
[[1,2],[3,4],[5]],
[[1,2,3],[4],[5,6]],
].each do |x|
p x, x.to_h, “—”
end

Kind regards

robert


#9

On Jan 31, 2:49 pm, Roger P. removed_email_address@domain.invalid wrote:

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?

The variety of possible good definitions make this hard to define. So
it’s understandable that is is not in Ruby core, though being able to
convert back and forth between Hash#to_a and Array#to_h makes the most
sense.

I was thinking about it some more and I was thinking about how the
various options might be addressed. So I came up with this:

Converts an associative array into a hash.

a = [ [:a,1], [:b,2] ]

a.to_h #=> { :a=>1, :b=>2 }

When a mixed or multi-element accociative array

is used, the result is as follows:

a = [ [:a,1,2], [:b,2], [:c], :d ]

a.to_h #=> { :a=>[1,2], :b=>2, :c=>nil, :d=>nil }

If the fist entry of the subelements is the same, then

the values will be merged using #concat.

a = [ [:a,1,2], [:a,3], [:a,4], [:a], :a ]

a.to_h #=> { :a=>[1,2,3,4,nil,nil] }

The +mode+ can be set to effect the result. If it is

set to +:array+ or +true+ then trailing arrays

will be kept. Eg.

a = [ [:a,1,2], [:b,3], [:c] ]

a.to_h(true) #=> { :a=>[1,2], :b=>[3], :c=>[] }

Setting the mode to +:splat+ will produce the same result

as calling +Hash[*array]+.

a = [:a,1,:b,2,:c]

a.to_h:splat #=> { :a=>1, :b=>2, :c=>nil }

Setting the mode to +:flat+ will produce the same result

as calling +Hash[*array.flatten]+.

a = [:a,1,[:b,2,:c]]

a.to_h:flat #=> { :a=>1, :b=>2, :c=>nil }

def to_h(mode=nil)
case mode
when :splat
a = dup
a << nil if a.size % 2 == 1
Hash[*a]
when :flat
a = flatten
a << nil if a.size % 2 == 1
Hash[*a]
when :array, True
h = {}
each do |k,*v|
h[k] ||= []
h[k].concat(v)
end
h
else
h = {}
each do |k,*v|
h[k] ||= []
h[k].concat(v)
end
h.each do |k,v|
h[k] = v[0] if v.size < 2
end
h
end
end

By using a +mode+ we can offer a variety of common means of
conversion.

Of course, we could just make them all separate methods, ie. #to_h,
#to_h_array, #to_h_splat, #to_h_flat. Maybe that is better? But then
that seems a bit more limiting, less dynamic, less open for new modes
or multiple labels for a single mode, and the method names look funny
(imo).

Thoughts?

T.


#10

On Feb 6, 9:35 am, Robert K. removed_email_address@domain.invalid wrote:

But then
that seems a bit more limiting, less dynamic, less open for new modes
or multiple labels for a single mode, and the method names look funny

Not at all: you can simply add another method.

True, but adding a new method is a “bigger deal” than just adding
another parameter option.

But I like your idea, I could create the different methods and then
dispatch from to_h, offering the best of both options.

    else
      arr += 1
    end
  end
end

I’m not sure. On one hand I like it, though I am hesitant about it b/c
it means a whole pass over the array upfront, it won’t be very fast.
What do you think about the performance characteristics? On the other
hand, it means the one method #to_h will do quite different things
depending on the form of the data structure passed to it. Is that a
good idea?

T.


#11

On Jan 31, 1:49 pm, Roger P. removed_email_address@domain.invalid wrote:

Not that I would find it useful at all, but is there is a Hash#to_a
should there not be an Array#to_h?
Thanks!
-=r

Posted viahttp://www.ruby-forum.com/.

irb(main):003:0> Hash[ *[:foo,22, :bar,44] ]
=> {:foo=>22, :bar=>44}


#12

As I recall, part of the problem is the question
of what it would mean; for example, given this:

[“a”,“b”,“c”,“d”].to_h

is it

[“a” => “b”, “c” => “d”]

This could be achieved by means of Hash[*array]:

a = [“a”,“b”,“c”,“d”]
Hash[*a]
=> {“a”=>“b”, “c”=>“d”}


#13

It’s fairly trivial, isn’t it?

class Array
def to_h
Hash[*self]
end
end
=> nil

[1,2,3,4].to_h
=> {1=>2, 3=>4}


#14

On 06.02.2009 18:24, Trans wrote:

a number of to_h_ methods. The rule I follow is to create
methods and classes to do one thing good.

But then
that seems a bit more limiting, less dynamic, less open for new modes
or multiple labels for a single mode, and the method names look funny
Not at all: you can simply add another method.

True, but adding a new method is a “bigger deal” than just adding
another parameter option.

Not for me.

But I like your idea, I could create the different methods and then
dispatch from to_h, offering the best of both options.

Exactly.

    else
      arr += 1
    end
  end
end

I’m not sure. On one hand I like it, though I am hesitant about it b/c
it means a whole pass over the array upfront, it won’t be very fast.

But you get automatism. If you know the structure beforehand, you can
call one of the other methods. That’s the nice thing about this
approach: all these methods do one thing good:

What do you think about the performance characteristics? On the other
hand, it means the one method #to_h will do quite different things
depending on the form of the data structure passed to it. Is that a
good idea?

Yes. I view it differently: to_h always makes the same thing which can
be described as “analyze contents of the Enum and select a proper
algorithm”. All other methods do the actual conversions.

If you lump everything into a single method then you cannot use them
separately. I would dig up quotes about not using these mode parameters
if I had more time right now but I’m in a hurry unfortunately. Trust
me, parameters that change a methods behavior are inferior to having
separate methods.

Kind regards

robert


#15

Sorry David I’ve since read the other posts :slight_smile: there must be SOME good
solution to mail catchup. Guess for now I just have to read a whole
thread before I reply to any of it :slight_smile:

Blog: http://random8.zenunit.com/
Learn rails: http://sensei.zenunit.com/


#16

Trust me, parameters that change a methods behavior are inferior to
having separate methods.

One can read Robert C. Martin’s “Clean Code” for the same advice and
many more. A devastating read for me.

mfg, simon … l


#17

Hi –

On Sat, 7 Feb 2009, Julian L. wrote:

a lot over the years. As I recall, part of the problem is the question
[0 => “a”, 1 => “b”, …]
=> nil

[1,2,3,4].to_h
=> {1=>2, 3=>4}

Yes, if that’s the semantics you want. If you’re inclined toward
thinking it should use the array indices as hash keys and the array
values as hash values, then that implementation wouldn’t work. My
point was that there’s been debate about the semantics – i.e., what
Array#to_h should actually do.

David


David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!


#18

Yes, if that’s the semantics you want. If you’re inclined toward
thinking it should use the array indices as hash keys and the array
values as hash values, then that implementation wouldn’t work.

With that constructor at hand, it wouldn’t be too difficult to get
that behaviour though:

a = [[1,2],[3,4]]

Hash[(0…(a.size - 1)).zip(a)]
=> {0=>[1, 2], 1=>[3, 4]}

Looks like php.


#19

Hi –

On Sat, 7 Feb 2009, Tom L. wrote:

Yes, if that’s the semantics you want. If you’re inclined toward
thinking it should use the array indices as hash keys and the array
values as hash values, then that implementation wouldn’t work.

With that constructor at hand, it wouldn’t be too difficult to get
that behaviour though:

a = [[1,2],[3,4]]

Hash[(0…(a.size - 1)).zip(a)]
=> {0=>[1, 2], 1=>[3, 4]}

None of this is hard to do without Array#to_h, but there’s still the
question of what Array#to_h would do if it existed.

David


David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!