From: Brian Candler Date: 2003-07-22T05:00:53+09:00 Subject: Re: Proposal: Array#to_h, to simplify hash generation --xHFwDpU9dbj6ez1V Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Tue, Jul 22, 2003 at 02:24:35AM +0900, Simon Strandgaard wrote: > On Tue, 22 Jul 2003 02:57:40 +0900, Brian Candler wrote: > > > On Tue, Jul 22, 2003 at 01:43:46AM +0900, Brian Candler wrote: > >> The bit I don't like is Hash#<< taking an array of two elements, rather than > >> having two arguments > > > > I forgot about using Method#arity. New version attached... Brian. > > > > class Array # ditto for Enumerable > > def collect(dest=Array,*args) > > res = dest.new(*args) > > Maybe its better to do the #new outside collect ? Yes, I worked that out in parallel with you :-) Thinking while walking home, the pattern foo.collect_into(bar) {|v| v} seems to be pretty useful, so I think that should be the default if no block is provided. Then arr.to_h would be just: arr.collect_into({}) which seems pretty intuitive to me. And h1.update(h2) would be equivalent to h2.collect_into(h1) which is also pretty clear. Since not all objects necessarily have a '<<' method then maybe it's worth having that as a parameter as well. The attached code lets you do that, or guesses at a suitable method if one isn't provided. Cheers, Brian. --xHFwDpU9dbj6ez1V Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="collect_into.rb" #!/usr/local/bin/ruby -w module Enumerable def collect_into(obj=[], concat=nil, iter=:each) unless concat concat = [:<<, :[]=, :store ].find { |m| obj.respond_to?(m) } end nargs = obj.method(concat).arity if block_given? if nargs == 1 send(iter) { |item| obj.send(concat, yield(item)) } else send(iter) { |item| obj.send(concat, *yield(item)) } end else if nargs == 1 send(iter) { |item| obj.send(concat, item) } else send(iter) { |item| obj.send(concat, *item) } end end obj end end if __FILE__ == $0 require 'test/unit' class CollectIntoTest < Test::Unit::TestCase ARR = [["foo",99],["wibble",nil],[Fixnum,/burble/]] ARR2 = ["fred","jim","bill"] HSH = {"fred"=>35, "boing"=>"ert", "zxcv"=>"qwerty"} HSH2 = {"Hawaii"=>50, "Beverly Hills"=>90210} # Case 1: collect without block, concat method arity 1 def test_noblock_1 r1 = ARR.collect {|x| x} r2 = ARR.collect_into assert_equal(r1, r2) r1 = HSH.collect {|x| x} r2 = HSH.collect_into assert_equal(r1, r2) r1 = "mytest" + ARR2.join r2 = ARR2.collect_into("mytest") assert_equal(r1, r2) end # Case 2: collect without block, concat method arity >1 def test_noblock_2 r1 = {} ARR.each { |k,v| r1[k] = v } r2 = ARR.collect_into({}) # method []= is implied assert_equal(r1, r2) r1 = HSH.dup.update(HSH2) r2 = HSH2.collect_into(HSH.dup) assert_equal(r1, r2) end # Case 3: collect with block, concat method arity 1 def test_block_1 r1 = ["zero"] (1..5).each { |i| r1 << (i*i) } r2 = (1..5).collect_into(["zero"]) { |i| i*i } assert_equal(r1, r2) r1 = [] ARR2.each_with_index { |x,i| r1 << "#{i}:#{x}" } r2 = ARR2.collect_into([],:<<,:each_with_index) { |x,i| "#{i}:#{x}" } assert_equal(r1, r2) end # Case 4: collect with block, concat method arity >1 def test_block_2 r1 = HSH.invert r2 = HSH.collect_into({}) { |k,v| [v,k] } assert_equal(r1, r2) end end end # if __FILE__ == $0 --xHFwDpU9dbj6ez1V--