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