Issue #7241 has been reported by nathan.f77 (Nathan Broadbent). ---------------------------------------- Feature #7241: Enumerable#to_h proposal https://bugs.ruby-lang.org/issues/7241 Author: nathan.f77 (Nathan Broadbent) Status: Open Priority: Normal Assignee: Category: core Target version: I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. This means that I often write code like: [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } I'm proposing an `Enumerable#to_h` method that would let me write: [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation. I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value. As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful. I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that: hash.to_a.to_h == hash # => true (See example 2) I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3) Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5) Heres an example implementation that I would be happy to rewrite in C if necessary: module Enumerable def to_h(default_or_sym = nil) if block_given? hash = if Proc === default_or_sym Hash.new(&default_or_sym) else Hash.new(default_or_sym) end self.each do |el| yield hash, el end elsif !default_or_sym.nil? hash = {} self.each do |el| hash[el] = el.send(default_or_sym) end else return Hash[*self.to_a.flatten(1)] end hash end end Examples ---------------------------------------------- # 1) Build a hash from array elements [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } => {1=>2, 2=>4, 3=>6, 4=>8, 5=>10} # 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h) [[1, 2], [3, 4], [5, 6]].to_h => {1=>2, 3=>4, 5=>6} # 3) Build a hash by calling a method on each array element ["String", "Another String"].to_h(:size) => {"String"=>6, "Another String"=>14} # 4) Hash with default value [4,5,6,5].to_h(0) {|h, el| h[el] += el } => {4=>4, 5=>10, 6=>6} # 5) Hash with default value returned from Proc default_proc = -> hash, key { hash[key] = "go fish: #{key}" } [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! } => {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"} Thanks for your time, and please let me know your thoughts! Best, Nathan Broadbent
on 2012-10-29 23:24
on 2012-10-30 00:19
On Tue, Oct 30, 2012 at 07:23:29AM +0900, nathan.f77 (Nathan Broadbent) wrote: > Assignee: > Category: core > Target version: > > > I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. > This means that I often write code like: > > [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } 1.9.3p194 :001 > [1,2,3,4].each_with_object({}) { |x,o| o[x] = x ** 2 } => {1=>1, 2=>4, 3=>9, 4=>16} 1.9.3p194 :002 >
on 2012-10-30 00:32
Issue #7241 has been updated by v_krishna (Vijay Ramesh).
Or
1.9.3-p0 :001 > Hash[ [1,2,3,4,5].map{|el| [el, el*2]} ]
=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}
----------------------------------------
Feature #7241: Enumerable#to_h proposal
https://bugs.ruby-lang.org/issues/7241#change-31935
Author: nathan.f77 (Nathan Broadbent)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version:
I often use the `inject` method to build a hash, but I always find it
annoying when I need to return the hash at the end of the block.
This means that I often write code like:
[1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash }
I'm proposing an `Enumerable#to_h` method that would let me write:
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would
not be in favor of his implementation.
I believe the implementation should be similar to `inject`, so that the
hash object and next element are passed to the block. The main
difference to the `inject` method is that we would be modifying the hash
in place, instead of relying on the block's return value.
As well as providing support for the case above, I have also considered
other cases where the `to_h` method would be useful.
I thought it would be useful if symmetry were provided for the
`Hash#to_a` method, such that:
hash.to_a.to_h == hash # => true
(See example 2)
I've allowed developers to provide a symbol instead of a block, so that
each element in the collection will be passed to that named method. (See
example 3)
Finally, hashes can be given a default value, or a Proc that returns the
default value. (See examples 4 & 5)
Heres an example implementation that I would be happy to rewrite in C if
necessary:
module Enumerable
def to_h(default_or_sym = nil)
if block_given?
hash = if Proc === default_or_sym
Hash.new(&default_or_sym)
else
Hash.new(default_or_sym)
end
self.each do |el|
yield hash, el
end
elsif !default_or_sym.nil?
hash = {}
self.each do |el|
hash[el] = el.send(default_or_sym)
end
else
return Hash[*self.to_a.flatten(1)]
end
hash
end
end
Examples
----------------------------------------------
# 1) Build a hash from array elements
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}
# 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h)
[[1, 2], [3, 4], [5, 6]].to_h
=> {1=>2, 3=>4, 5=>6}
# 3) Build a hash by calling a method on each array element
["String", "Another String"].to_h(:size)
=> {"String"=>6, "Another String"=>14}
# 4) Hash with default value
[4,5,6,5].to_h(0) {|h, el| h[el] += el }
=> {4=>4, 5=>10, 6=>6}
# 5) Hash with default value returned from Proc
default_proc = -> hash, key { hash[key] = "go fish: #{key}" }
[4,5,6].to_h(default_proc) {|h, el| h[el].upcase! }
=> {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"}
Thanks for your time, and please let me know your thoughts!
Best,
Nathan Broadbent
on 2012-10-30 00:34
Thanks! Sorry, I didn't know about each_with_object.
Do you think it would still be worth shortening
`each_with_object(Hash.new([])) { ... }` to `to_h([]) { ... }`, and are
any
of the other cases worth supporting?
Best,
Nathan
On Tue, Oct 30, 2012 at 12:18 PM, Aaron Patterson
on 2012-10-30 00:37
Issue #7241 has been updated by matz (Yukihiro Matsumoto). Status changed from Open to Rejected ---------------------------------------- Feature #7241: Enumerable#to_h proposal https://bugs.ruby-lang.org/issues/7241#change-31937 Author: nathan.f77 (Nathan Broadbent) Status: Rejected Priority: Normal Assignee: Category: core Target version: I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. This means that I often write code like: [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } I'm proposing an `Enumerable#to_h` method that would let me write: [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation. I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value. As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful. I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that: hash.to_a.to_h == hash # => true (See example 2) I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3) Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5) Heres an example implementation that I would be happy to rewrite in C if necessary: module Enumerable def to_h(default_or_sym = nil) if block_given? hash = if Proc === default_or_sym Hash.new(&default_or_sym) else Hash.new(default_or_sym) end self.each do |el| yield hash, el end elsif !default_or_sym.nil? hash = {} self.each do |el| hash[el] = el.send(default_or_sym) end else return Hash[*self.to_a.flatten(1)] end hash end end Examples ---------------------------------------------- # 1) Build a hash from array elements [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } => {1=>2, 2=>4, 3=>6, 4=>8, 5=>10} # 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h) [[1, 2], [3, 4], [5, 6]].to_h => {1=>2, 3=>4, 5=>6} # 3) Build a hash by calling a method on each array element ["String", "Another String"].to_h(:size) => {"String"=>6, "Another String"=>14} # 4) Hash with default value [4,5,6,5].to_h(0) {|h, el| h[el] += el } => {4=>4, 5=>10, 6=>6} # 5) Hash with default value returned from Proc default_proc = -> hash, key { hash[key] = "go fish: #{key}" } [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! } => {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"} Thanks for your time, and please let me know your thoughts! Best, Nathan Broadbent
on 2012-10-30 00:37
Issue #7241 has been updated by matz (Yukihiro Matsumoto). Your idea of to_h is interesting, but it adds too much behavior in one method. Besides that, since to_s, to_a, to_i etc. are used for implicit conversion, to_h is not a proper name for the method. Nice try, we will wait for next one. Matz. ---------------------------------------- Feature #7241: Enumerable#to_h proposal https://bugs.ruby-lang.org/issues/7241#change-31936 Author: nathan.f77 (Nathan Broadbent) Status: Open Priority: Normal Assignee: Category: core Target version: I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. This means that I often write code like: [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } I'm proposing an `Enumerable#to_h` method that would let me write: [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation. I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value. As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful. I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that: hash.to_a.to_h == hash # => true (See example 2) I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3) Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5) Heres an example implementation that I would be happy to rewrite in C if necessary: module Enumerable def to_h(default_or_sym = nil) if block_given? hash = if Proc === default_or_sym Hash.new(&default_or_sym) else Hash.new(default_or_sym) end self.each do |el| yield hash, el end elsif !default_or_sym.nil? hash = {} self.each do |el| hash[el] = el.send(default_or_sym) end else return Hash[*self.to_a.flatten(1)] end hash end end Examples ---------------------------------------------- # 1) Build a hash from array elements [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } => {1=>2, 2=>4, 3=>6, 4=>8, 5=>10} # 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h) [[1, 2], [3, 4], [5, 6]].to_h => {1=>2, 3=>4, 5=>6} # 3) Build a hash by calling a method on each array element ["String", "Another String"].to_h(:size) => {"String"=>6, "Another String"=>14} # 4) Hash with default value [4,5,6,5].to_h(0) {|h, el| h[el] += el } => {4=>4, 5=>10, 6=>6} # 5) Hash with default value returned from Proc default_proc = -> hash, key { hash[key] = "go fish: #{key}" } [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! } => {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"} Thanks for your time, and please let me know your thoughts! Best, Nathan Broadbent
on 2012-10-30 00:48
OK, no problem! Thanks for your response!
A bit unrelated, but is it strange that each_with_object and inject have
a
different order for the block params?
[1,2,3].inject({}) {|obj, el| obj[el] = el * 2; obj } #=>
{1=>2,
2=>4, 3=>6}
[1,2,3].each_with_object({}) {|obj, el| obj[el] = el * 2 } #=>
NoMethodError: undefined method `*' for {}:Hash
[1,2,3].each_with_object({}) {|el, obj| obj[el] = el * 2 } #=>
{1=>2,
2=>4, 3=>6}
On Tue, Oct 30, 2012 at 12:37 PM, matz (Yukihiro Matsumoto) <
on 2012-10-30 11:59
Issue #7241 has been updated by rosenfeld (Rodrigo Rosenfeld Rosas). Maybe .hash_map? each_with_object is a too long name for a very common needed method. Many have asked for a method like it (including me) because they couldn't find "each_with_object" and they ended up learning here after asking for such a method. Maybe "hash_map" could be a better name for this. matz (Yukihiro Matsumoto) wrote: > Your idea of to_h is interesting, but it adds too much behavior in one method. > Besides that, since to_s, to_a, to_i etc. are used for implicit conversion, to_h is not a proper name for the method. > > Nice try, we will wait for next one. > > Matz. ---------------------------------------- Feature #7241: Enumerable#to_h proposal https://bugs.ruby-lang.org/issues/7241#change-31973 Author: nathan.f77 (Nathan Broadbent) Status: Rejected Priority: Normal Assignee: Category: core Target version: I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. This means that I often write code like: [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } I'm proposing an `Enumerable#to_h` method that would let me write: [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation. I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value. As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful. I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that: hash.to_a.to_h == hash # => true (See example 2) I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3) Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5) Heres an example implementation that I would be happy to rewrite in C if necessary: module Enumerable def to_h(default_or_sym = nil) if block_given? hash = if Proc === default_or_sym Hash.new(&default_or_sym) else Hash.new(default_or_sym) end self.each do |el| yield hash, el end elsif !default_or_sym.nil? hash = {} self.each do |el| hash[el] = el.send(default_or_sym) end else return Hash[*self.to_a.flatten(1)] end hash end end Examples ---------------------------------------------- # 1) Build a hash from array elements [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } => {1=>2, 2=>4, 3=>6, 4=>8, 5=>10} # 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h) [[1, 2], [3, 4], [5, 6]].to_h => {1=>2, 3=>4, 5=>6} # 3) Build a hash by calling a method on each array element ["String", "Another String"].to_h(:size) => {"String"=>6, "Another String"=>14} # 4) Hash with default value [4,5,6,5].to_h(0) {|h, el| h[el] += el } => {4=>4, 5=>10, 6=>6} # 5) Hash with default value returned from Proc default_proc = -> hash, key { hash[key] = "go fish: #{key}" } [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! } => {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"} Thanks for your time, and please let me know your thoughts! Best, Nathan Broadbent
on 2012-10-30 19:24
On Tue, Oct 30, 2012 at 07:58:33PM +0900, rosenfeld (Rodrigo Rosenfeld Rosas) wrote: > > Issue #7241 has been updated by rosenfeld (Rodrigo Rosenfeld Rosas). > > > Maybe .hash_map? each_with_object is a too long name for a very common needed method. Many have asked for a method like it (including me) because they couldn't find "each_with_object" and they ended up learning here after asking for such a method. > > Maybe "hash_map" could be a better name for this. `each_with_object` isn't specific to hashes, and isn't doing list translation like `map` does. IOW, it sounds perfect for ActiveSupport. ;-)
on 2012-10-30 20:23
> `each_with_object` isn't specific to hashes, and isn't doing list translation like `map` does. I think `each_with_hash` would be really useful. Not only for cases where you would normally use `each_with_object({})`, but it would also be much nicer to write `each_with_hash([])` instead of `each_with_object(Hash.new([]))`. I had another idea that I'd like to mention: Since you'd often be using default hash values with `each_with_hash`, I would find it useful if there was an easy way to convert the default value into a proc that sets the hash value when a key is looked up, instead of writing `-> hash, key { hash[key] = <default> }`. My intention is to be able to write code like: `[1, 2, 1].each_with_hash([], true) {|h, v| h[v] << v ** 2 }`, where the `true` arg would convert the default value into the proc above. (Personal preference, but I don't really like `+= []`). Does anyone else think that could be useful? > IOW, it sounds perfect for ActiveSupport. ;-) Let me know if you think `each_with_hash` is a good idea, and if it should be added to ActiveSupport. I could open a pull request on Rails or start a new Issue for Ruby :) Best, Nathan
on 2012-10-30 22:01
Em 30-10-2012 16:23, Aaron Patterson escreveu: > IOW, it sounds perfect for ActiveSupport. ;-) > I often have this requirement and I guess others have it as well. There are two problems with each_with_object in my opinion: 1 - you can't find it easily in the docs when you're looking for some way to "inject" a Hash without worrying about the result of the block; hash_map would be easier to find in the docs for newcomers (to each_with_object I mean, like I was less then an year ago if I remember correctly); 2 - it is a too long name. See examples below: hash = a_long_array_name_as_I_usually_use_for_my_variables.each_with_object({}){|(name, url), h| h[name] = url } h = {}; a_long_array_name_as_I_usually_use_for_my_variables.each{|(name, url)| h[name] = url }; hash = h Often in my methods I don't really need that extra (; hash = h) so it is usually much shorter when I don't use each_with_object. With proposed method: hash = a_long_array_name_as_I_usually_use_for_my_variables.hash_map{|h, (name, url)| h[name] = url } Notice that I changed the order of the arguments for the block. It makes more sense to me this way, just like inject. I know this is subjective but I find the last example better to read ;) Cheers, Rodrigo.
on 2012-11-01 00:07
Hi everyone, Please see the pull request that I've opened on Rails ActiveSupport, to add an `each_with_hash` method: https://github.com/rails/rails/pull/8088 @matz: Do you think this `each_with_hash` implementation could be added to Ruby, or is it better as a Rails ActiveSupport extension? Best, Nathan
on 2012-11-01 03:30
Almost no one uses #each_with_object as it is. #each_with_hash is hardly better. We need a short method name. Moreover I don't think this method's behavior is really the best approach to the real use case. On Wed, Oct 31, 2012 at 7:07 PM, Nathan Broadbent <nathan.f77@gmail.com>wrote: > Nathan > -- Sorry, says the barman, we don't serve neutrinos. A neutrino walks into a bar. Trans <transfire@gmail.com> 7r4n5.com http://7r4n5.com
on 2012-11-01 04:08
> > Almost no one uses #each_with_object as it is. #each_with_hash is hardly > better. We need a short method name. Moreover I don't think this method's > behavior is really the best approach to the real use case. > It's true that each_with_object doesn't seem to be used too much, but when it is used, the object is usually a hash (for 90% of the cases in Rails, at least.) I think that each_with_hash should be provided for when you want to map an enumerable onto a Hash, but I think that there should also be a 'to_h' method on Array for when you just want to *convert* an Array into a hash. I think 'to_h' would be most useful if it supported the behaviour of both `Hash[ arr ]`, and 'Hash[ *arr ]'. I'm on my phone at the moment, but here's how I could see that working: def to_h if self.all? {|el| el.respond_to? :each && el.size == 2 } Hash[self] else Hash[*self] end end We could just let Hash[] handle any invalid input.
on 2012-11-01 04:18
Hi,
In message "Re: [ruby-core:48690] Re: [ruby-trunk - Feature #7241]
Enumerable#to_h proposal"
on Thu, 1 Nov 2012 08:07:11 +0900, Nathan Broadbent
<nathan.f77@gmail.com> writes:
|@matz: Do you think this `each_with_hash` implementation could be added to
|Ruby, or is it better as a Rails ActiveSupport extension?
I think it should go in to ActiveSupport first.
matz.
on 2012-11-01 04:27
> > I think it should go in to ActiveSupport first. > > matz. > Thanks for your reply! The pull request has just been rejected on ActiveSupport, so I guess that's the end of this discussion :) Thank you for Ruby, by the way, it's a beautiful language! Best, Nathan
on 2012-11-01 09:10
Issue #7241 has been updated by alexeymuranov (Alexey Muranov). Just in case, here is some relevant discussion on StackOverflow with benchmarks: http://stackoverflow.com/questions/3230863/ruby-ra... ---------------------------------------- Feature #7241: Enumerable#to_h proposal https://bugs.ruby-lang.org/issues/7241#change-32146 Author: nathan.f77 (Nathan Broadbent) Status: Rejected Priority: Normal Assignee: Category: core Target version: I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block. This means that I often write code like: [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash } I'm proposing an `Enumerable#to_h` method that would let me write: [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation. I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value. As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful. I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that: hash.to_a.to_h == hash # => true (See example 2) I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3) Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5) Heres an example implementation that I would be happy to rewrite in C if necessary: module Enumerable def to_h(default_or_sym = nil) if block_given? hash = if Proc === default_or_sym Hash.new(&default_or_sym) else Hash.new(default_or_sym) end self.each do |el| yield hash, el end elsif !default_or_sym.nil? hash = {} self.each do |el| hash[el] = el.send(default_or_sym) end else return Hash[*self.to_a.flatten(1)] end hash end end Examples ---------------------------------------------- # 1) Build a hash from array elements [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 } => {1=>2, 2=>4, 3=>6, 4=>8, 5=>10} # 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h) [[1, 2], [3, 4], [5, 6]].to_h => {1=>2, 3=>4, 5=>6} # 3) Build a hash by calling a method on each array element ["String", "Another String"].to_h(:size) => {"String"=>6, "Another String"=>14} # 4) Hash with default value [4,5,6,5].to_h(0) {|h, el| h[el] += el } => {4=>4, 5=>10, 6=>6} # 5) Hash with default value returned from Proc default_proc = -> hash, key { hash[key] = "go fish: #{key}" } [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! } => {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"} Thanks for your time, and please let me know your thoughts! Best, Nathan Broadbent
on 2012-11-01 09:49
Issue #7241 has been updated by trans (Thomas Sawyer).
=begin
I wouldn't say it is over. See #4151.
I still like:
module Enumerable
def each_with(x={})
each{ |e| yield(x,e) }
x
end
end
Is #each_with a better name?
=end
----------------------------------------
Feature #7241: Enumerable#to_h proposal
https://bugs.ruby-lang.org/issues/7241#change-32147
Author: nathan.f77 (Nathan Broadbent)
Status: Rejected
Priority: Normal
Assignee:
Category: core
Target version:
I often use the `inject` method to build a hash, but I always find it
annoying when I need to return the hash at the end of the block.
This means that I often write code like:
[1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash }
I'm proposing an `Enumerable#to_h` method that would let me write:
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would
not be in favor of his implementation.
I believe the implementation should be similar to `inject`, so that the
hash object and next element are passed to the block. The main
difference to the `inject` method is that we would be modifying the hash
in place, instead of relying on the block's return value.
As well as providing support for the case above, I have also considered
other cases where the `to_h` method would be useful.
I thought it would be useful if symmetry were provided for the
`Hash#to_a` method, such that:
hash.to_a.to_h == hash # => true
(See example 2)
I've allowed developers to provide a symbol instead of a block, so that
each element in the collection will be passed to that named method. (See
example 3)
Finally, hashes can be given a default value, or a Proc that returns the
default value. (See examples 4 & 5)
Heres an example implementation that I would be happy to rewrite in C if
necessary:
module Enumerable
def to_h(default_or_sym = nil)
if block_given?
hash = if Proc === default_or_sym
Hash.new(&default_or_sym)
else
Hash.new(default_or_sym)
end
self.each do |el|
yield hash, el
end
elsif !default_or_sym.nil?
hash = {}
self.each do |el|
hash[el] = el.send(default_or_sym)
end
else
return Hash[*self.to_a.flatten(1)]
end
hash
end
end
Examples
----------------------------------------------
# 1) Build a hash from array elements
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}
# 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h)
[[1, 2], [3, 4], [5, 6]].to_h
=> {1=>2, 3=>4, 5=>6}
# 3) Build a hash by calling a method on each array element
["String", "Another String"].to_h(:size)
=> {"String"=>6, "Another String"=>14}
# 4) Hash with default value
[4,5,6,5].to_h(0) {|h, el| h[el] += el }
=> {4=>4, 5=>10, 6=>6}
# 5) Hash with default value returned from Proc
default_proc = -> hash, key { hash[key] = "go fish: #{key}" }
[4,5,6].to_h(default_proc) {|h, el| h[el].upcase! }
=> {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"}
Thanks for your time, and please let me know your thoughts!
Best,
Nathan Broadbent
on 2012-11-01 11:06
> > I wouldn't say it is over. See #4151. ... Is #each_with a better name? > Has anyone suggested `map_to`? I think `map_to` has a clearer intention than `each_with`, because you're mapping the collection onto something, and then returning it. I don't really like the `each` part of `each_with_object`, because `array.each` just returns the array. Since we usually use `each` to iterate, and `map` to build an array, I think `map_to(<object>)` might make sense. How does this look: [1, 2, 3].map_to({}) { |e, hash| hash[e] = e ** 2 } I'd also propose a `map_to_hash` method. It's longer than `map_to({})`, but I think it's nicer to read: [1, 2, 3].map_to_hash { |e, hash| hash[e] = e ** 2 } `map_to_hash(0)` would also be nicer than `map_to(Hash.new(0))`. What do you think?
on 2012-11-10 16:47
Issue #7241 has been updated by jballanc (Joshua Ballanco).
=begin
Clojure has a function (({into})) that might fit the bill. An equivalent
Ruby implementation might look something like the following:
class Hash
alias :<< :merge!
end
module Enumerable
def into(coll)
coll = coll.dup
each do |elem|
coll << yield(elem)
end
coll
end
end
chars = (97..107).into({}) { |i| { i => i.chr } }
p chars
require 'prime'
prime_chars = chars.into([]) { |k, v| k.prime? ? v : nil }
p prime_chars.compact
char_string = chars.into("") { |k, v| "#{k}=>#{v}, " }
p char_string
=end
----------------------------------------
Feature #7241: Enumerable#to_h proposal
https://bugs.ruby-lang.org/issues/7241#change-32755
Author: nathan.f77 (Nathan Broadbent)
Status: Rejected
Priority: Normal
Assignee:
Category: core
Target version:
I often use the `inject` method to build a hash, but I always find it
annoying when I need to return the hash at the end of the block.
This means that I often write code like:
[1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash }
I'm proposing an `Enumerable#to_h` method that would let me write:
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would
not be in favor of his implementation.
I believe the implementation should be similar to `inject`, so that the
hash object and next element are passed to the block. The main
difference to the `inject` method is that we would be modifying the hash
in place, instead of relying on the block's return value.
As well as providing support for the case above, I have also considered
other cases where the `to_h` method would be useful.
I thought it would be useful if symmetry were provided for the
`Hash#to_a` method, such that:
hash.to_a.to_h == hash # => true
(See example 2)
I've allowed developers to provide a symbol instead of a block, so that
each element in the collection will be passed to that named method. (See
example 3)
Finally, hashes can be given a default value, or a Proc that returns the
default value. (See examples 4 & 5)
Heres an example implementation that I would be happy to rewrite in C if
necessary:
module Enumerable
def to_h(default_or_sym = nil)
if block_given?
hash = if Proc === default_or_sym
Hash.new(&default_or_sym)
else
Hash.new(default_or_sym)
end
self.each do |el|
yield hash, el
end
elsif !default_or_sym.nil?
hash = {}
self.each do |el|
hash[el] = el.send(default_or_sym)
end
else
return Hash[*self.to_a.flatten(1)]
end
hash
end
end
Examples
----------------------------------------------
# 1) Build a hash from array elements
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}
# 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h)
[[1, 2], [3, 4], [5, 6]].to_h
=> {1=>2, 3=>4, 5=>6}
# 3) Build a hash by calling a method on each array element
["String", "Another String"].to_h(:size)
=> {"String"=>6, "Another String"=>14}
# 4) Hash with default value
[4,5,6,5].to_h(0) {|h, el| h[el] += el }
=> {4=>4, 5=>10, 6=>6}
# 5) Hash with default value returned from Proc
default_proc = -> hash, key { hash[key] = "go fish: #{key}" }
[4,5,6].to_h(default_proc) {|h, el| h[el].upcase! }
=> {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"}
Thanks for your time, and please let me know your thoughts!
Best,
Nathan Broadbent
on 2012-11-11 09:59
On 2012/11/11 0:47, jballanc (Joshua Ballanco) wrote: > > Issue #7241 has been updated by jballanc (Joshua Ballanco). > > > =begin > Clojure has a function (({into})) that might fit the bill. This indeed looks very promising. > An equivalent Ruby implementation might look something like the following: > > class Hash > alias :<< :merge! > end I might be wrong, but my guess is that constructing lots of one-key/value hashes isn't very efficient. Two-element arrays should be quite a bit more efficient. So we could define this as follows (in the end in C, but here just in Ruby): class Hash def << (other) case other.class when Array store(other[0], other[1]) when Hash merge! other end self end end (some additional tweaks may be needed for Array-like and Hash-like objects). > chars = (97..107).into({}) { |i| { i => i.chr } } > p chars > > require 'prime' > prime_chars = chars.into([]) { |k, v| k.prime? ? v : nil } > p prime_chars.compact It would be great to have a version that avoided "compact". Or maybe only that version would be okay? This would use "concat" instead of merge! (with Hash#concat an alias for Hash#merge!). Because neither Hashes nor Strings can be nested, there would actually not be any difference for those, but for Array, the preceeding code could be simplified to: require 'prime' prime_chars = chars.into___([]) { |k, v| k.prime? ? [v] : [] } I often want a "collect" method where I'm not forced to collect exactly one item per item of the original collection. If collect weren't an alias to map, I think it would even make a lot of sense to use the word "collect" for this (map: one-to-one, collect: one-to-many). Regards, Martin.
on 2012-11-11 10:42
> > Clojure has a function (({into})) that might fit the bill. >> > > This indeed looks very promising. I like the sound of 'into', but am not sure about appending results with the '<<' operator. If Hash had '<<' and '+' aliases for 'update' and 'merge' (respectively), we might as well give 'map' an optional argument, and call: [1,2,3].map({}) {|i| { i => i ** 2 } } And if Hash#update accepted a two-element array, we could do: [1,2,3].map({}) {|i| [i, i ** 2] } So I like the 'into' name, but I think it would be more useful as an alias for 'each_with_object', instead of just 'map' with an argument for the base object. > I often want a "collect" method where I'm not forced to collect exactly > one item per item of the original collection. If collect weren't an alias > to map, I think it would even make a lot of sense to use the word "collect" > for this (map: one-to-one, collect: one-to-many). > Ruby has a 'flat_map' method (aliased as 'collect_concat') that flattens the first level of a returned array, so you can append multiple results, and don't need to use compact. See http://ruby-doc.org/core-1.9.3/Enumerable.html#met... [1,nil,2].flat_map {|i| i ? [i] : [] } #=> [1, 2] Best, Nathan
on 2012-11-12 14:10
Issue #7241 has been updated by rosenfeld (Rodrigo Rosenfeld Rosas).
I like "into". But I'd vote it to be an alias to "each_of_object" as I
even prefer "into" instead of "each_with" or "map_with". I'd also vote
for the order of the closure arguments to be changed.
I read "doubles = numbers.into({}){|h, n| h[n] = 2 * n }" as "assign to
double the numbers into a hash indexed by each number having the double
as value".
----------------------------------------
Feature #7241: Enumerable#to_h proposal
https://bugs.ruby-lang.org/issues/7241#change-32814
Author: nathan.f77 (Nathan Broadbent)
Status: Rejected
Priority: Normal
Assignee:
Category: core
Target version:
I often use the `inject` method to build a hash, but I always find it
annoying when I need to return the hash at the end of the block.
This means that I often write code like:
[1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash }
I'm proposing an `Enumerable#to_h` method that would let me write:
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would
not be in favor of his implementation.
I believe the implementation should be similar to `inject`, so that the
hash object and next element are passed to the block. The main
difference to the `inject` method is that we would be modifying the hash
in place, instead of relying on the block's return value.
As well as providing support for the case above, I have also considered
other cases where the `to_h` method would be useful.
I thought it would be useful if symmetry were provided for the
`Hash#to_a` method, such that:
hash.to_a.to_h == hash # => true
(See example 2)
I've allowed developers to provide a symbol instead of a block, so that
each element in the collection will be passed to that named method. (See
example 3)
Finally, hashes can be given a default value, or a Proc that returns the
default value. (See examples 4 & 5)
Heres an example implementation that I would be happy to rewrite in C if
necessary:
module Enumerable
def to_h(default_or_sym = nil)
if block_given?
hash = if Proc === default_or_sym
Hash.new(&default_or_sym)
else
Hash.new(default_or_sym)
end
self.each do |el|
yield hash, el
end
elsif !default_or_sym.nil?
hash = {}
self.each do |el|
hash[el] = el.send(default_or_sym)
end
else
return Hash[*self.to_a.flatten(1)]
end
hash
end
end
Examples
----------------------------------------------
# 1) Build a hash from array elements
[1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }
=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}
# 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h)
[[1, 2], [3, 4], [5, 6]].to_h
=> {1=>2, 3=>4, 5=>6}
# 3) Build a hash by calling a method on each array element
["String", "Another String"].to_h(:size)
=> {"String"=>6, "Another String"=>14}
# 4) Hash with default value
[4,5,6,5].to_h(0) {|h, el| h[el] += el }
=> {4=>4, 5=>10, 6=>6}
# 5) Hash with default value returned from Proc
default_proc = -> hash, key { hash[key] = "go fish: #{key}" }
[4,5,6].to_h(default_proc) {|h, el| h[el].upcase! }
=> {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"}
Thanks for your time, and please let me know your thoughts!
Best,
Nathan Broadbent
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.