[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Array#to_h

Roger Pack

1/31/2009 7:49:00 PM

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 via http://www.ruby-....

18 Answers

David A. Black

1/31/2009 8:04:00 PM

0

Hi --

On Sun, 1 Feb 2009, Roger Pack 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 :-) 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.r...
Coming in 2009: The Well-Grounded Rubyist (http://manning....)

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

Jesús Gabriel y Galán

1/31/2009 8:05:00 PM

0

On Sat, Jan 31, 2009 at 8:49 PM, Roger Pack <rogerpack2005@gmail.com> 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.

Joshua Ballanco

1/31/2009 8:10:00 PM

0

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

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

- Josh


On Jan 31, 2009, at 2:49 PM, Roger Pack 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 via http://www.ruby-....
>


James Coglan

1/31/2009 8:47:00 PM

0

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

2009/1/31 Joshua Ballanco <jballanc@gmail.com>

> See here:
> http://www.fivesevensix.com/posts/2005/05/20/...
>
> ...and here:
> http://drawohara.com/post/70998078/ruby-array-to-...
>
> - 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-rub...

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.

Sean O'Halpin

2/1/2009 5:32:00 PM

0

On Sat, Jan 31, 2009 at 7:49 PM, Roger Pack <rogerpack2005@gmail.com> 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

Robert Klemme

2/1/2009 5:42:00 PM

0

On 31.01.2009 21:03, David A. Black wrote:
> Hi --
>
> On Sun, 1 Feb 2009, Roger Pack 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 :-) 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", ....]
>
> ?

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

Trans

2/6/2009 2:13:00 PM

0

On Jan 31, 2:49=A0pm, Roger Pack <rogerpack2...@gmail.com> 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 =3D [ [:a,1], [:b,2] ]
# a.to_h #=3D> { :a=3D>1, :b=3D>2 }
#
# When a mixed or multi-element accociative array
# is used, the result is as follows:
#
# a =3D [ [:a,1,2], [:b,2], [:c], :d ]
# a.to_h #=3D> { :a=3D>[1,2], :b=3D>2, :c=3D>nil, :d=3D>nil }
#
# If the fist entry of the subelements is the same, then
# the values will be merged using #concat.
#
# a =3D [ [:a,1,2], [:a,3], [:a,4], [:a], :a ]
# a.to_h #=3D> { :a=3D>[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 =3D [ [:a,1,2], [:b,3], [:c] ]
# a.to_h(true) #=3D> { :a=3D>[1,2], :b=3D>[3], :c=3D>[] }
#
# Setting the mode to +:splat+ will produce the same result
# as calling +Hash[*array]+.
#
# a =3D [:a,1,:b,2,:c]
# a.to_h:splat #=3D> { :a=3D>1, :b=3D>2, :c=3D>nil }
#
# Setting the mode to +:flat+ will produce the same result
# as calling +Hash[*array.flatten]+.
#
# a =3D [:a,1,[:b,2,:c]]
# a.to_h:flat #=3D> { :a=3D>1, :b=3D>2, :c=3D>nil }

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

Robert Klemme

2/6/2009 2:35:00 PM

0

2009/2/6 Trans <transfire@gmail.com>:
> On Jan 31, 2:49 pm, Roger Pack <rogerpack2...@gmail.com> 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.

<snip/>

> 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_<alg> 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


--
remember.guy do |as, often| as.you_can - without end

Trans

2/6/2009 5:25:00 PM

0



On Feb 6, 9:35=A0am, Robert Klemme <shortcut...@googlemail.com> wrote:

> This is generally considered better practice over switching behavior
> of a method with an argument. =A0Just think about the length of the
> method which increases in with the number of different algorithms
> (modes). =A0I 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_<alg> methods. =A0The 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.

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

> This is how I'd approach it:
>
> module Enumerable
> =A0 def to_h
> =A0 =A0 pairs =3D arr =3D 0
>
> =A0 =A0 each do |e|
> =A0 =A0 =A0 if Array =3D=3D=3D e
> =A0 =A0 =A0 =A0 if e.size <=3D 2
> =A0 =A0 =A0 =A0 =A0 pairs +=3D 1
> =A0 =A0 =A0 =A0 else
> =A0 =A0 =A0 =A0 =A0 arr +=3D 1
> =A0 =A0 =A0 =A0 end
> =A0 =A0 =A0 end
> =A0 =A0 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.

William James

2/6/2009 5:39:00 PM

0

On Jan 31, 1:49 pm, Roger Pack <rogerpack2...@gmail.com> 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-....

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