Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

RCR: Array#to_h

0 views
Skip to first unread message

Shannon Fang

unread,
Oct 24, 2005, 11:10:18 PM10/24/05
to
Hi there,

I have registered 2 accounts for RCRkive, but all failed... So I posted
here.

Abstract

A simple method that construct a hash from an array. Just like Hash#to_a
return an array from hash.

Problem

Consider this cenario: I programmed a multi-threaded web page downloader.
One of its input is an array of urls to download. In the program, I wanted
to use a hash like {'http://ruby-lang.org/index.html' => '200'}, i.e., use a
hash to record the return code of the http request. This way I can avoid
re-download a page, or miss a page.

For the user of this download program, it is much easier to use an Array
instead of a Hash:

d = WebPageDownloader.new
d.links = IO.readlines('list.txt')

Hence, in the program we want to do:

@links = @links.to_h

Proposal

Add a method to_h (not to_hash) in the Array class, so that user can:

a = [1, 2, 3, 4, 5]
p a.to_h { |i, v| v * 2} --> {5=>10, 1=>2, 2=>4, 3=>6, 4=>8}
p a.to_h(3) --> {5=>3, 1=>3, 2=>3, 3=>3, 4=>3}


Anaysis

This is very convenient for users who need this feature, and it will not
affect behavior of the Array class in anyway for those do not need this
feature.

Please refer to RCR278, which is similar but not same.

Implementation

class Array
public
def to_h(value = nil)
_hash = {}
self.each_index do |i|
v = self[i]
if block_given? then
_hash[v] = yield(i, v)
else
_hash[v] = value
end
end
_hash
end
end

Please comment.

Thanks,
Shannon


Marcel Molina Jr.

unread,
Oct 24, 2005, 11:16:04 PM10/24/05
to
On Tue, Oct 25, 2005 at 12:10:18PM +0900, Shannon Fang wrote:
> Abstract
>
> A simple method that construct a hash from an array. Just like Hash#to_a
> return an array from hash.

Having your proposed to_h accept a block is a nice idea but as it is you can
do the non-block version quite easily:

>> Hash[*%w(a b c d)]
=> {"a"=>"b", "c"=>"d"}
>> keys = %w(a b c d)
=> ["a", "b", "c", "d"]
>> values = [1, 2, 3, 4]
=> [1, 2, 3, 4]
>> Hash[*keys.zip(values).flatten]
=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4}

marcel
--
Marcel Molina Jr. <mar...@vernix.org>


nobuyoshi nakada

unread,
Oct 24, 2005, 11:48:40 PM10/24/05
to
Hi,

At Tue, 25 Oct 2005 12:10:18 +0900,
Shannon Fang wrote in [ruby-talk:162399]:


> Abstract
>
> A simple method that construct a hash from an array. Just like Hash#to_a
> return an array from hash.

Agreed here, but

> Proposal
>
> Add a method to_h (not to_hash) in the Array class, so that user can:
>
> a = [1, 2, 3, 4, 5]
> p a.to_h { |i, v| v * 2} --> {5=>10, 1=>2, 2=>4, 3=>6, 4=>8}
> p a.to_h(3) --> {5=>3, 1=>3, 2=>3, 3=>3, 4=>3}

I expect that method as:

h = {"foo"=>1,"bar"=>2}
a = h.to_a # => [["foo", 1], ["bar", 2]]
a.to_h # => {"foo"=>1,"bar"=>2} == h

module Enumerable
def to_h
hash = {}
each {|key, value| hash[key] = value}
hash
end
end

--
Nobu Nakada


Trans

unread,
Oct 25, 2005, 12:36:27 AM10/25/05
to
I think the proposal is over specialized. Why should the array elements
neccessarily become keys?

Nobu, I agree except 1) key,value in Enumerable? 2) other clearly
polymorphic solution:

module Enumerable
def to_h
hash = {}

each_with_index {|value,index| hash[i] = value}
hash
end
end

Hence hash key <-> array index. And it is by definition "Enumerable".

T.

Shannon Fang

unread,
Oct 25, 2005, 12:38:06 AM10/25/05
to
The block may be useful because you may want to, for example, do some
calculation, or even lookup the database...

Shannon

Shannon Fang

unread,
Oct 25, 2005, 12:46:33 AM10/25/05
to
Ok, we may consider Array#to_h as a true reverse operation of Hash#to_a,
however, I think the feature I proposed has different usage. May be anyone
has a better name for easy understanding?

Also, I am not very clear about this code:

> module Enumerable
> def to_h
> hash = {}
> each {|key, value| hash[key] = value}
> hash
> end
> end
>

Array mixed Eumerable, hence, if use the above code, we have:

arr = [a, b, c, d, e]
arr.to_h => {1 => a, 2 => b, 3 => c, 4 => d, 5 =>e}

Which seems not very useful, I think the to_h operation of array should use
array VALUE as the hash KEY.

Shannon


>From: nobuyoshi nakada <nobuyosh...@ge.com>
>Reply-To: ruby...@ruby-lang.org
>To: ruby...@ruby-lang.org (ruby-talk ML)
>Subject: Re: RCR: Array#to_h

Shannon Fang

unread,
Oct 25, 2005, 12:52:07 AM10/25/05
to
>I think the proposal is over specialized. Why should the array elements
>neccessarily become keys?

This is actually the useful feature! I already said not everybody need it...
However, I don't see any need to map array key to hash key:

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

b = {1, 2, 3, 4, 5}

a[0] = 1
b[0] = 1

why we need to convert a to b?? The reason to use a hash is for fast access,
not by using sequential index!

Shannon

Trans

unread,
Oct 25, 2005, 12:54:35 AM10/25/05
to
Actually this has been bugging me as I currently have these:

module Enumerable

# Produces a hash from an Enumerable with index for keys.
#
# a1 = [ :a, :b ]
# a1.to_h #=> { 0=>:a, 1=>:b }
#
def to_h( &blk )
h = {}
if block_given?
each_with_index{ |e,i| h[i] = blk.call(e,i) }
else
each_with_index{ |e,i| h[i] = e }
end
h
end

end

class Array

# Produces a hash for an Array, or two arrays.
# It is just like Enumerbale#to_h but with an
# extra feature: If an array is given as the
# values, it is zipped with the receiver,
# to produce the hash.
#
# a1 = [ :a, :b ]
# a2 = [ 1, 2 ]
# a1.to_h(a2) #=> { :a=>1, :b=>2 }
#
def to_h(values=nil)
h = {}
if values
size.times{ |i| h[at(i)] = values.at(i) }
else
each_with_index{ |e,i| h[i] = e }
end
h
end

# Converts an associative array into a hash.
#
# a = [ [:a,1], [:b,2] ]
# a.assoc_to_h #=> { :a=>1, :b=>2 }
#
# a = [ [:a,1,2], [:b,3,4] ]
# a.assoc_to_h(true) #=> { :a=>[1,2], :b=>[3,4] }
#
def assoc_to_h(arrayed=nil)
h = {}
if arrayed
each{ |e| h[e.first] = e.slice(1..-1) }
else
each{ |e| h[e.first] = e.last }
end
h
end

end

I wish there was a good way just to have a single Array#to_h, but it
doesn;t seem reasonable. Perhaps Enumerable#to_h could be #to_hash?

T.

Shannon Fang

unread,
Oct 25, 2005, 1:05:07 AM10/25/05
to
I am just reading the PickAxe 2e recently... According to Dave, to_h means a
representation of the object in hash, however to_hash means this object is
inherently compatible with hash, like to_i and to_int...

We do need a good name for it :D

Shannon


>From: "Trans" <tran...@gmail.com>
>Reply-To: ruby...@ruby-lang.org
>To: ruby...@ruby-lang.org (ruby-talk ML)
>Subject: Re: RCR: Array#to_h

nobuyoshi nakada

unread,
Oct 25, 2005, 1:39:20 AM10/25/05
to
Hi,

At Tue, 25 Oct 2005 13:37:01 +0900,
Trans wrote in [ruby-talk:162411]:


> I think the proposal is over specialized. Why should the array elements
> neccessarily become keys?

Yes, agreed, and I doubt that there is a solution which
satisfy everyone for this issue.

--
Nobu Nakada


Trans

unread,
Oct 25, 2005, 1:58:24 AM10/25/05
to
BTW have you tried:

module Enumerable

# Like <tt>#map</tt>/<tt>#collect</tt>, but it generates a Hash. The
block
# is expected to return two values: the key and the value for the new
hash.
#
# numbers = (1..3)
# squares = numbers.graph { |n| [n, n*n] } # { 1=>1, 2=>4, 3=>9
}
# sq_roots = numbers.graph { |n| [n*n, n] } # { 1=>1, 4=>2, 9=>3
}
#
#--
# Credit for original version goes to Zallus Kanite and Gavin
Sinclair.
#++
def graph(&yld)
if yld
inject({}) do |h,kv|
nk, nv = yld[*kv]
h[nk] = nv
h
end
else
Hash[*self.to_a.flatten]
end
end

end

Yukihiro Matsumoto

unread,
Oct 25, 2005, 2:17:00 AM10/25/05
to
Hi,

In message "Re: RCR: Array#to_h"


on Tue, 25 Oct 2005 12:10:18 +0900, "Shannon Fang" <xrf...@hotmail.com> writes:

|Abstract
|
|A simple method that construct a hash from an array. Just like Hash#to_a
|return an array from hash.
|
|Problem
|
|Consider this cenario: I programmed a multi-threaded web page downloader.
|One of its input is an array of urls to download. In the program, I wanted
|to use a hash like {'http://ruby-lang.org/index.html' => '200'}, i.e., use a
|hash to record the return code of the http request. This way I can avoid
|re-download a page, or miss a page.
|
|For the user of this download program, it is much easier to use an Array
|instead of a Hash:
|
|d = WebPageDownloader.new
|d.links = IO.readlines('list.txt')
|
|Hence, in the program we want to do:
|
|@links = @links.to_h
|
|Proposal
|
|Add a method to_h (not to_hash) in the Array class, so that user can:
|
|a = [1, 2, 3, 4, 5]
|p a.to_h { |i, v| v * 2} --> {5=>10, 1=>2, 2=>4, 3=>6, 4=>8}
|p a.to_h(3) --> {5=>3, 1=>3, 2=>3, 3=>3, 4=>3}

Rejected. Proposal Array#to_h(value=nil) does not actually solve the
problem above. If you want to have _this_ to_h, you need to have
a proper usecase.

matz.


Daniel Sheppard

unread,
Oct 25, 2005, 2:27:30 AM10/25/05
to
Maybe what we're actually after here is the equivalent to map/collect
that produces a hash instead. I really don't like the name "hash_map",
but it was either that or "hashify". If you want the index behaviour,
you can do that with an Enumerable.

module Enumerable
def hash_map
h = {}
each do |x|
k,*v = yield(*x)
h[k] = *v
end
h
end
end
class Hash
def hash_map!(&block)
replace(hash_map(&block))
end
end

#now we can create a hash with all the values double that of a previous
hash:

hash = {'a'=>1, 'b'=> 2}
hash2 = hash.hash_map { |k,v| [k,(v*2)]}

#=> {"a"=>2, "b"=>4}

#or form a hash from an array:

arr = [1,2,3,4,5,6]
hash3 = arr.hash_map { |x| [x, x * 5] }

#=>{5=>25, 6=>30, 1=>5, 2=>10, 3=>15, 4=>20}

The main problem with the above is the fact that the block needs to
return two values, and it's quite awkward to do that.

#####################################################################################
This email has been scanned by MailMarshal, an email content filter.
#####################################################################################


Trans

unread,
Oct 25, 2005, 2:32:06 AM10/25/05
to

Shannon Fang wrote:
> I am just reading the PickAxe 2e recently... According to Dave, to_h means a
> representation of the object in hash, however to_hash means this object is
> inherently compatible with hash, like to_i and to_int...

Right. But your reminder provokes me a solution, albiet it's a bit
tilted. Nonetheless I don't see any reason Array#to_hash can't be
defined. After all it is in effect a hash of restricted key (integers).
Even though that does't align with Enumerable#to_h, that's okay.

class Array
alias :to_hash :to_h

Thanks,
T.

Shannon Fang

unread,
Oct 25, 2005, 3:02:06 AM10/25/05
to
Hi Matz,

>Rejected. Proposal Array#to_h(value=nil) does not actually solve the
>problem above. If you want to have _this_ to_h, you need to have
>a proper usecase.

I don't know what you mean the "problem above"... My purpose is to have a
convenient way to convert array to hash for *indexing* or *fast searching*
purpose.

This indeed is a bit ad-hoc, but it is useful in may cases. I deliberately
wanted to map array value (not index) to hash key... otherwise I think it is
not useful (i.e., map array key => hash key).

I think there might be 2 types of RCRs, one is more "scientific", those deep
into the design of language. Another is more "engineering", those add a
method/feature to a single class for convinience, like Array#nitems.

I think for the latter one, the criteria to accept/reject, is if it is
useful for "lots of" Ruby programmers..., if that's your criteria, then I
think I will wait to see if others want this :D
else, if one of your criterion is to make Ruby "pure", all those things
should belong to a library not the language itself (or core classes), then
pls also let me know, so that I can have a better idea when propose RCR in
the future.

Thanks,
Shannon


>From: Yukihiro Matsumoto <ma...@ruby-lang.org>
>Reply-To: ruby...@ruby-lang.org
>To: ruby...@ruby-lang.org (ruby-talk ML)
>Subject: Re: RCR: Array#to_h

Robert Klemme

unread,
Oct 25, 2005, 3:28:48 AM10/25/05
to
Shannon Fang wrote:
> Hi Matz,
>
>> Rejected. Proposal Array#to_h(value=nil) does not actually solve the
>> problem above. If you want to have _this_ to_h, you need to have
>> a proper usecase.
>
> I don't know what you mean the "problem above"... My purpose is to
> have a convenient way to convert array to hash for *indexing* or
> *fast searching* purpose.

If you just want a fast access to those elements a Set is sufficient - and
you can use #to_set already:

>> require 'set'
=> true
>> a=%w{foo bar baz}
=> ["foo", "bar", "baz"]
>> s=a.to_set
=> #<Set: {"baz", "foo", "bar"}>
>> s.include? "foo"
=> true
>> s.include? "fo"
=> false

> This indeed is a bit ad-hoc, but it is useful in may cases.

I think Matz just asked you to present these use cases. Apart from your
general description I couldn't find one in the thread. Did I miss
something?

> I
> deliberately wanted to map array value (not index) to hash key...
> otherwise I think it is not useful (i.e., map array key => hash key).

Problem is, that there are other conversions that are at least equally
reasonable (e.g. the one Nobu presented). IMHO there is not a single
reasonable way to implement Array#to_h so it's better to leave it out.
Maybe it's just a naming issue though.

Kind regards

robert

Shannon Fang

unread,
Oct 25, 2005, 4:16:22 AM10/25/05
to
Hi Robert,

1) Set won't work. I am not only test if an element exist or not, I want to
assign value to the key to track its status.

2) I have not write any RCR before, so I may make mistakes. I will certainly
propose a good use case, when I have a complete one.

3) >Problem is, that there are other conversions that are at least equally


>reasonable (e.g. the one Nobu presented). IMHO there is not a single
>reasonable way to implement Array#to_h so it's better to leave it out.
>Maybe it's just a naming issue though.

Yes I agree it is a name issue.

Thanks,
Shannon


>From: "Robert Klemme" <bob....@gmx.net>
>Reply-To: ruby...@ruby-lang.org
>To: ruby...@ruby-lang.org (ruby-talk ML)
>Subject: Re: RCR: Array#to_h

David A. Black

unread,
Oct 25, 2005, 6:13:14 AM10/25/05
to
Hi --

But then what's the point? :-)

This raises the old question of why hashes have both keys and
numerical indices. I've always maintained that they shouldn't.


David

--
David A. Black
dbl...@wobblini.net


David A. Black

unread,
Oct 25, 2005, 6:16:22 AM10/25/05
to
Hi --

On Tue, 25 Oct 2005, Shannon Fang wrote:

> Hi there,
>
> I have registered 2 accounts for RCRkive, but all failed... So I posted here.

You'll have to give me more details if you want it fixed :-)

> Abstract
>
> A simple method that construct a hash from an array. Just like Hash#to_a
> return an array from hash.

See http://www.rcrchive.net/rejected.html#rcr12

Shannon Fang

unread,
Oct 25, 2005, 7:52:02 AM10/25/05
to
Hi David,

I have registered RCR account using xrf...@hotmail.com and
sa...@spamweed.com. Please tell me when I can use one of these :)

Thank you!
Shannon


>From: "David A. Black" <dbl...@wobblini.net>
>Reply-To: ruby...@ruby-lang.org
>To: ruby...@ruby-lang.org (ruby-talk ML)
>Subject: Re: RCR: Array#to_h

Trans

unread,
Oct 25, 2005, 8:51:52 AM10/25/05
to

David A. Black wrote:
> Hi --
>
> On Tue, 25 Oct 2005, Trans wrote:
>
> > I think the proposal is over specialized. Why should the array elements
> > neccessarily become keys?
> >
> > Nobu, I agree except 1) key,value in Enumerable? 2) other clearly
> > polymorphic solution:
> >
> > module Enumerable
> > def to_h
> > hash = {}
> > each_with_index {|value,index| hash[i] = value}
> > hash
> > end
> > end
> >
> > Hence hash key <-> array index. And it is by definition "Enumerable".
>
> But then what's the point? :-)

Perhaps little. But you may simply need a hash based off an enumerable
and this could be one step it getting it:

('a'..'c').to_h.invert #=> { 'a'=>1, 'b'=>2, 'c'=>3 }

And of course it's consistant with what Enumerable is.

> This raises the old question of why hashes have both keys and
> numerical indices. I've always maintained that they shouldn't.

Right. Hash doesn't really have index, it's a fake --a counter only. As
you'll recall, we've talked about this before, and if I recall
correctly, we even came to a shared conclusion, which is something I've
been meaning to ask Matz:

have you given anymore consideration to deprecating
Enumerabl#each_with_index in favor of Enumerable#each_with_counter;
Array would retain it's own #each_with_index.

T.

Steven Lumos

unread,
Oct 26, 2005, 12:23:42 AM10/26/05
to

And the block version:

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


=> [1, 2, 3, 4, 5]

>> Hash[*a.map {|e| [e, 2*e]}.flatten]
=> {5=>10, 1=>2, 2=>4, 3=>6, 4=>8}

Steve

0 new messages