RCR: Array#to_h

S

Shannon Fang

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
if block_given? then
_hash[v] = yield(i, v)
else
_hash[v] = value
end
end
_hash
end
end

Please comment.

Thanks,
Shannon
 
M

Marcel Molina Jr.

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
 
N

nobuyoshi nakada

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
 
T

Trans

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 = value}
hash
end
end

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

T.
 
S

Shannon Fang

The block may be useful because you may want to, for example, do some
calculation, or even lookup the database...

Shannon

From: "Marcel Molina Jr." <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: Re: RCR: Array#to_h
Date: Tue, 25 Oct 2005 12:16:04 +0900

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
 
S

Shannon Fang

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 <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: Re: RCR: Array#to_h
Date: Tue, 25 Oct 2005 12:48:40 +0900

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
 
S

Shannon Fang

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

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 = value}
hash
end
end

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

T.
 
T

Trans

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 = blk.call(e,i) }
else
each_with_index{ |e,i| h = 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 = 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.
 
S

Shannon Fang

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
 
N

nobuyoshi nakada

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.
 
T

Trans

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
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: RCR: Array#to_h"

|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.
 
T

Trans

Shannon said:
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.
 
S

Shannon Fang

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 <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: Re: RCR: Array#to_h
Date: Tue, 25 Oct 2005 15:17:00 +0900

Hi,

In message "Re: RCR: Array#to_h"
on Tue, 25 Oct 2005 12:10:18 +0900, "Shannon Fang"

|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.
 
R

Robert Klemme

Shannon said:
Hi Matz,


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
=> # said:
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
 
S

Shannon Fang

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" <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: Re: RCR: Array#to_h
Date: Tue, 25 Oct 2005 16:32:02 +0900

Shannon said:
Hi Matz,


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
=> # said:
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
 
D

David A. Black

Hi --

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 = value}
hash
end
end

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


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
 
S

Shannon Fang

Hi David,

I have registered RCR account using (e-mail address removed) and
(e-mail address removed). Please tell me when I can use one of these :)

Thank you!
Shannon
 
T

Trans

David said:
Hi --

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 = 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.
 

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,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top