# Enumerable#categorize

enumerable \$B\$+\$i(B hash \$B\$r@8@.\$9\$k%a%=%C%I\$H\$7\$F(B
Enumerable#categorize \$B\$rDI2C\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!#(B

\$B\$^\$@(B Ruby \$B\$G;n\$7\$K<BAu\$7\$?CJ3,\$G!“(BC
\$B\$G\$O<BAu\$7\$F\$\$\$J\$\$\$N\$G!”\$9\$0\$K\$H\$\$\$&OC\$G\$O\$"\$j\$^\$;\$s\$,!#(B

categorize \$B%a%=%C%I\$O(B enumerable \$B\$NMWAG\$r%+%F%4%jJ,3d\$7\$F(B
hash \$B\$r@8@.\$9\$k%a%=%C%I\$G\$9!#(B

\$B\$?\$H\$(\$P!“0J2<\$N\$h\$&\$JG[Ns\$,\$”\$C\$?\$H\$7\$^\$7\$g\$&!#(B
\$B\$GFI\$_9~\$s\$@7k2L\$3\$&\$\$\$&G[Ns\$,F@\$i\$l\$?\$H9M\$(\$F\$/\$@\$5\$\$(B)

ary = [
[“matz”, “Yukihiro M.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],
[“naruse”, “NARUSE, Yui”],
]

\$B\$3\$&\$\$\$&%G!<%?\$+\$i!“3FMWAG\$NBh0lMWAG\$+\$iBhFsMWAG!”(B
\$B\$“\$k\$\$\$OBhFsMWAG\$+\$iBh0lMWAG\$X\$N%O%C%7%e\$r:n\$k\$H\$\$\$&\$N\$O\$”\$j\$,\$A\$JOC\$G\$9!#(B

\$B9,\$\$\$K\$7\$FBh0lMWAG\$+\$iBhFsMWAG\$X\$N%O%C%7%e\$O(B Hash[ary]
\$B\$G:n\$l\$k\$h\$&\$K\$J\$C\$?\$N\$G!“(B
\$B5U\$NBhFsMWAG\$+\$iBh0lMWAG\$X\$N%O%C%7%e\$K\$D\$\$\$F9M\$(\$^\$7\$g\$&!#(B
(\$BF1@+F1L>\$r9MN8\$7\$F!”%O%C%7%e\$NCM\$OBh0lMWAG\$NG[Ns\$H\$7\$^\$7\$g\$&(B)

\$B\$3\$l\$rF@\$k\$K\$O8=:_!“;DG0\$J;v\$K!”<+J,\$G%k!<%W\$r=q\$/I,MW\$,\$"\$j\$^\$9!#(B

h = {}
ary.each {|a, b|
(h[b] ||= []) << a
}
pp h
#=> {“Yukihiro M.”=>[“matz”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],

Enumerable#categorize \$B\$O\$3\$l\$r\$R\$H\$D\$N%a%=%C%I8F\$S=P\$7\$G<B8=\$7\$^\$9!#(B

h = ary.categorize(1, 0)
pp h
#=> {“Yukihiro M.”=>[“matz”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],

\$B0z?t\$N(B 1, 0 \$B\$,2?\$r0UL#\$9\$k\$+\$H\$\$\$&\$H!“(Benumerable \$B\$NMWAG\$+\$i(B
\$B%O%C%7%e\$N%-!<5Z\$SCM\$r<h\$j=P\$9;XDj\$G\$9!#(B
\$B6qBNE*\$K\$O(B ary \$B\$N3FMWAG\$KBP\$7!”(B[] \$B%a%=%C%I\$r8F\$S=P\$7!“(B
\$B\$=\$N0z?t\$K0z\$-EO\$7\$F%-!<(B/\$BCM\$rF@\$^\$9!#(B
\$B\$D\$^\$j!”(B[“matz”, “Yukihiro M.”][1] \$B\$H\$7\$F(B “Yukihiro
Matsumoto” \$B\$H\$\$\$&%-!<\$rF@\$F!"(B
[“matz”, “Yukihiro M.”][0] \$B\$H\$7\$F(B “matz”
\$B\$H\$\$\$&CM\$rF@\$k\$o\$1\$G\$9!#(B

Enumerable#categorize
\$B\$N4pK\E*\$J;H\$\$J}\$O\$3\$3\$^\$G\$J\$N\$G\$9\$,!“\$\$\$/\$D\$+DI2C5!G=\$,\$”\$j\$^\$9!#(B

• \$B0z?t\$K(B Proc \$B%%V%8%'%/%H(B (\$B@53N\$K\$O(B call
\$B%a%=%C%I\$r;}\$C\$?%
%V%8%‘%/%H(B) \$B\$r;XDj\$9\$k\$H!“(B
[] \$B%a%=%C%I\$G\$J\$/!”\$=\$N(B Proc
\$B%*%V%8%’%/%H\$G%-!<(B/\$BCM\$r<h\$j=P\$9(B

\$B\$D\$^\$j!"(Bary.categorize(1, 0) \$B\$O(B
ary.categorize(lambda {|elt| elt[1] }, lambda {|elt| elt[0] })
\$B\$HF1\$8\$G\$9!#(B

Hash[ary]
\$B\$N\$h\$&\$J5!G=\$,2?2s\$b%j%/%(%9%H\$5\$l\$J\$,\$iD9G/<B8=\$5\$l\$J\$+\$C\$?M}M3\$O!“(B
\$BMWAG\$,D9\$5(B2\$B\$NG[Ns\$G\$”\$k\$H\$\$\$&2>Dj\$rCV\$\$\$?%a%=%C%I\$KBP\$9\$km4m0\$,\$“\$C\$?\$h\$&\$K;W\$(\$k\$N\$G\$9\$,!”(B
\$B\$3\$N(B Proc \$B%*%V%8%'%/%H\$K\$h\$k;XDj\$r2DG=\$K\$9\$k\$3\$H\$K\$h\$j!"(B
Enumerable#categorize \$B\$OMWAG\$KBP\$9\$k2>Dj\$rI,?\$G\$O\$J\$/\$7\$F\$\$\$^\$9!#(B

• \$B%M%9%H\$7\$?%O%C%7%e\$N@8@.(B

[ruby-talk:372481] \$B\$d(B [ruby-talk:288931]
\$B\$J\$I!“\$?\$^\$K\$”\$k\$N\$G\$9\$,!“(B
\$B%M%9%H\$7\$?%O%C%7%e\$,I,MW\$J;v\$,\$”\$j\$^\$9!#(B
Enumerable#categorize
\$B\$N%-!<\$r;XDj\$9\$k0z?t\$O!“<B\$O\$U\$?\$D0J>e;XDj\$G\$-\$F!”(B
\$B\$=\$&\$9\$k\$H%M%9%H\$7\$?%O%C%7%e\$r@8@.\$7\$^\$9!#(B

h = ary.categorize(lambda {|e| e[0][0] }, lambda {|e| e[0][1]}, 0)
pp h
#=> {“m”=>{“a”=>[“matz”]},
“n”=>{“o”=>[“nobu”], “a”=>[“naruse”]},
“a”=>{“k”=>[“akr”]},
“u”=>{“s”=>[“usa”]},
“k”=>{“o”=>[“ko1”]}}

• \$B%O%C%7%e\$NCM\$N8e=hM}(B

\$B%O%C%7%e\$NCM\$OG[Ns\$G\$9\$,!“\$=\$NG[Ns\$r%=!<%H\$7\$?\$\$\$J\$I\$N8e=hM}\$,\$7\$?\$\$\$3\$H\$,\$”\$j\$^\$9!#(B
\$B\$=\$3\$G!“(BEnumerable#categorize \$B\$K%V%m%C%/\$r\$D\$1\$k\$H!”(B
\$B@8@.\$7\$?G[Ns\$+\$iB>\$NCM\$KJQ49\$9\$k\$3\$H\$,\$G\$-\$^\$9!#(B

h = ary.categorize(lambda {|e| e[0][0] }, 1) {|ks, vs| vs.sort }
pp h’
{“m”=>[“Yukihiro M.”],
“a”=>[“Tanaka A.”],
“u”=>[“Usaku NAKAMURA”],

\$B\$^\$?!“;H\$&B&\$H\$7\$F\$OF1\$8%-!<\$KBP\$7\$F\$O\$R\$H\$D\$NCM\$7\$+\$J\$\$;v\$rCN\$C\$F\$\$\$F(B
\$BG[Ns\$K\$7\$?\$/\$J\$\$\$H\$+!”(B
\$B\$“\$k\$\$\$O9g7W!&:G>.CM!&:GBgCM!&J?6QCM\$,M_\$7\$\$\$H\$+!”\$^\$?C1\$KMWAG?t\$@\$1M_\$7\$\$\$H\$+!"(B
\$B\$=\$&\$\$\$&MQES\$K\$b;H\$(\$^\$9!#(B
(\$B\$3\$l\$i\$K\$D\$\$\$F\$O%a%b%j8zN(\$NE@\$+\$i<!\$K=R\$Y\$k(B :seed, :op, :update
\$B\$r;H\$&J}\$,E,@Z\$G\$9\$,(B)

h = ary.categorize(1, 0) {|ks, vs|
raise “duplicate keys: #{ks.inspcet}” if vs.length != 1
vs[0]
}
pp h
#=> {“Yukihiro M.”=>“matz”,
“Tanaka A.”=>“akr”,
“Usaku NAKAMURA”=>“usa”,
“NARUSE, Yui”=>“naruse”,

\$B\$J\$*!“%V%m%C%/0z?t\$N(B ks \$B\$O@8@.\$5\$l\$?CM\$KBP1~\$9\$k%-!<\$NG[Ns\$G!”(B
\$B>e5-\$NNc\$G\$O!"\$R\$H\$D\$NCM\$7\$+\$J\$\$\$O\$:\$J\$N\$K\$=\$&\$G\$J\$+\$C\$?\$H\$-\$N(B
\$BNc30%a%C%;!<%8\$K;H\$C\$F\$\$\$^\$9!#(B

• :seed, :op, :update \$B%*%W%7%g%s(B

\$B%a%b%j>CHq\$NET9g>e!“G[Ns\$r@8@.\$7\$?\$/\$J\$\$!”\$H\$\$\$&>u67\$b\$“\$j\$^\$9!#(B
\$BNc\$(\$P!”\$\$\$/\$D\$“\$k\$N\$+?t\$(\$?\$\$\$H\$\$\$&\$@\$1\$J\$i!”(B
\$BG[Ns\$r@8@.\$7\$F\$+\$i(B length \$B\$r8F\$S=P\$9\$N\$O\$+\$J\$jL5BL\$G\$9!#(B
\$B\$=\$N\$h\$&\$J>u67\$KBP1~\$9\$k\$?\$a!"G[Ns\$r@8@.\$9\$k\$+\$o\$j\$NA`:n\$r;XDj\$9\$k\$3\$H\$,\$G\$-\$^\$9!#(B

h = ary.categorize(lambda {|e| e[0][0] }, lambda {|e| 1 }, :op=>:+)
pp h
#=> {“m”=>1, “n”=>2, “a”=>1, “u”=>1, “k”=>1}

:seed \$B\$H(B :op \$B\$O\$@\$\$\$?\$\$(B inject \$B\$N\$h\$&\$JF0:n\$K\$J\$j\$^\$9!#(B
:update \$B\$O(B :op
\$B\$H\$[\$\F1\$8\$G\$9\$,!"0z?t\$,\$R\$H\$DB?\$/\$F!"CM\$KBP1~\$9\$k%-!<\$,M?\$(\$i\$l\$^\$9!#(B
(:seed \$B\$,M?\$(\$i\$l\$J\$\$;~\$K\$O!"3F%+%F%4%j\$N:G=i\$NCM\$,(B seed
\$B07\$\$\$K\$J\$j\$^\$9(B)

:op, :update \$B\$K;XDj\$7\$?\$b\$N\$O(B to_proc \$B\$GJQ49\$5\$l\$k\$N\$G!“(B
:op => :+ \$B\$H\$\$\$&\$N\$O!”(Blambda {|x,y| x + y } \$B\$N0UL#\$G\$9!#(B

• \$BJ#?t\$NCM\$r<h\$j=P\$9(B

enum \$B\$NMWAG\$,G[Ns\$d%O%C%7%e\$GB?\$/\$NMWAG\$r\$b\$D>l9g!“(B
\$B\$R\$H\$D\$G\$J\$/!”\$\$\$/\$D\$+\$NMWAG\$r<h\$j=P\$9\$3\$H\$,\$"\$j\$^\$9!#(B
\$B\$=\$N\$?\$a\$K!“G[Ns\$r;XDj\$7\$?>l9g\$K\$O:F5"E*\$KMWAG\$N<h\$j=P\$7\$r9T\$C\$F!”(B
\$B<h\$j=P\$7\$?CM\$rG[Ns\$K\$^\$H\$a\$^\$9!#(B

\$BA0=R\$N(B ary \$B\$O(B
2\$BMWAG\$7\$+\$J\$/!“\$”\$^\$j\$3\$l\$rE,MQ\$9\$k0UL#\$,\$J\$\$\$N\$G!“(B
http://coderepos.org/share/export/38695/lang/ruby/ruby-committers/ruby-committers.yml
\$B\$r;H\$&\$H!”\$3\$l\$O%O%C%7%e\$rMWAG\$H\$9\$k\$NG[Ns\$J\$N\$G!"(B

committers = open(“ruby-committers.yml”) {|f| YAML.load(f) }
pp committers.categorize(“account”, [“name”, “nick”]) {|ks, vs| vs[0]
}
#=> {“matz”=>[[“\$B>>K\9T90(B”, “\$B\$^\$D\$b\$H\$f\$-\$R\$m(B”, “Yukihiro
Matsumoto”], [“Matz”]],
“H_Konishi”=>[[“\$B>.@>90>-(B”, “KONISHI Hiromasa”], nil],
“aamine”=>[[“\$B@DLZJvO:(B”, “Minero A.”], [“\$B@DLZ\$5\$s(B”]],

\$B\$J\$I\$H\$G\$-!"(Bname \$B\$H(B nick \$B\$rN>J}<h\$j=P\$9\$3\$H\$,\$G\$-\$^\$9!#(B
(categorize \$B\$N0z?t\$,@0?t\$8\$c\$J\$/\$FJ8;zNs\$K\$J\$C\$F\$\$\$k\$N\$O(B ary
\$B\$NMWAG\$,J8;zNs\$r%-!<\$H\$9\$k%O%C%7%e\$@\$+\$i\$G\$9(B)

\$B\$I\$&\$G\$7\$g\$&\$+!#(B

\$BG[Ns\$+\$i(B hash \$B\$r@8@.\$9\$k%a%=%C%I\$O8=>u(B enum.group_by \$B\$d(B
Hash[assoc] \$B\$,\$“\$j\$^\$9\$,!”(B
categorize
\$B\$NA@\$\$=j\$H\$7\$F\$O!“\$=\$l\$i\$h\$j\$bDc%l%Y%k\$G9-\$\$1~MQ\$r;}\$D\$,!”\$\$\$/\$i\$+5-=R\$,D9\$/!“(B
\$B\$G\$b%k!<%W\$G5-=R\$9\$k\$h\$j\$O9b%l%Y%k\$G\$+\$J\$jC;\$\$!”\$H\$\$\$&\$"\$?\$j\$G\$9!#(B

\$B\$?\$H\$(\$P!“(B (1…6).group_by {|i| i % 3 } \$B\$O(B
(1…6).categorize(lambda {|e| e % 3}, lambda {|e| e}) \$B\$H<B8=\$G\$-!”(B
Hash[ [ [“a”, 100], [“b”, 200] ] ] \$B\$O(B
[ [“a”, 100], [“b”, 200] ].categorize(0, 1, :op=>lambda {|x,y| y })
\$B\$H<B8=\$G\$-\$^\$9!#(B
(\$B\$"\$k\$\$\$OG[Ns\$,\$G\$-\$A\$c\$&\$1\$I(B [ [“a”, 100], [“b”, 200]
].categorize(0, 1) {|ks, vs|
vs.last } \$B\$H\$+(B)

\$B;29M(B:

• RDB \$B\$N(B hash-join \$B%"%k%4%j%:%`(B
• SQL \$B\$N=8Ls4X?t(B
• MapReduce

Ruby \$B\$K\$h\$k;n83E*\$J<BAu(B:

module Enumerable

enum.

element.

nested.

hashes.

:update.

:price => 100},

:price => 300},

# {:fruit => “grapefruit”, :color => “yellow”, :taste =>

“tart”, :price => 200}]

# #=> {“sweet”=>{“yellow”=>[“banana”], “green”=>[“melon”]},

“tart”=>{“yellow”=>[“grapefruit”]}}

selectors.

# - object with +call+ method (procedure, etc.): extracts a value

from the element by calling the procedure with the element as an
argument.

# - array of selectors: make an array which contains the values

extracted by the selectors.

# - other object: extracts a value from the element using +[]+

method as +element[selector]+.

=> 100}

behavior.

# (If :seed option is not given, the first value for each category

is treated as an initial value.)

# :op option specifies a procedure to combine a seed and an element

into a next seed.

procedure.

# The default behavior, collecting all values as an array, is

implemented as follows.

# The block for +categorize+ method converts combined values to

final innermost hash values.

vs.length }

# #=> {“yellow”=>150.0, “green”=>300.0}

def categorize(*args, &reduce_proc)
opts = args.last.kind_of?(Hash) ? args.pop : {}
if args.length < 2
raise ArgumentError, “needs 2 or more arguments without option
hash (but #{args.length})”
end
value_selector = cat_selector_proc(args.pop)
key_selectors = args.map {|a| cat_selector_proc(a) }
has_seed = opts.include? :seed
seed_value = opts[:seed]
if opts.include?(:update) && opts.include?(:op)
raise ArgumentError, “both :op and :update option specified”
elsif opts.include? :update
update_proc = opts[:update].to_proc
elsif opts.include? :op
op_proc = opts[:op].to_proc
update_proc = lambda {|ks, s, v| op_proc.call(s, v) }
else
has_seed = true
seed_value = nil
update_proc = lambda {|ks, s, v| !s ? [v] : (s << v) }
end
result = {}
each {|*elts|
elt = elts.length <= 1 ? elts[0] : elts
ks = key_selectors.map {|ksel| ksel.call(elt) }
v = value_selector.call(elt)
h = result
0.upto(ks.length-2) {|i|
k = ks[i]
h[k] = {} if !h.include?(k)
h = h[k]
}
lastk = ks.last
if !h.include?(lastk)
if has_seed
h[lastk] = update_proc.call(ks, seed_value, v)
else
h[lastk] = v
end
else
h[lastk] = update_proc.call(ks, h[lastk], v)
end
}
if reduce_proc
cat_reduce(result, [], key_selectors.length-1, reduce_proc)
end
result
end

def cat_selector_proc(selector)
if selector.respond_to?(:call)
selector
elsif selector.respond_to? :to_ary
selector_procs = selector.to_ary.map {|sel| cat_selector_proc(sel)
}
lambda {|elt| selector_procs.map {|selproc| selproc.call(elt) } }
else
lambda {|elt| elt[selector] }
end
end
private :cat_selector_proc

def cat_reduce(hash, ks, nestlevel, reduce_proc)
if nestlevel.zero?
hash.each {|k, v|
ks << k
begin
hash[k] = reduce_proc.call(ks.dup, v)
ensure
ks.pop
end
}
else
hash.each {|k, h|
ks << k
begin
cat_reduce(h, ks, nestlevel-1, reduce_proc)
ensure
ks.pop
end
}
end
end
private :cat_reduce
end

(2010/11/27 18:45), Tanaka A. wrote:

\$B\$3\$&\$\$\$&%G!<%?\$+\$i!“3FMWAG\$NBh0lMWAG\$+\$iBhFsMWAG!”(B
\$B\$"\$k\$\$\$OBhFsMWAG\$+\$iBh0lMWAG\$X\$N%O%C%7%e\$r:n\$k\$H\$\$\$&\$N\$O\$"\$j\$,\$A\$JOC\$G\$9!#(B

\$B%K!<%:\$OM}2r\$G\$-\$^\$9!#(B

\$B9,\$\$\$K\$7\$FBh0lMWAG\$+\$iBhFsMWAG\$X\$N%O%C%7%e\$O(B Hash[ary]
\$B\$G:n\$l\$k\$h\$&\$K\$J\$C\$?\$N\$G!"(B
\$B5U\$NBhFsMWAG\$+\$iBh0lMWAG\$X\$N%O%C%7%e\$K\$D\$\$\$F9M\$(\$^\$7\$g\$&!#(B
(\$BF1@+F1L>\$r9MN8\$7\$F!"%O%C%7%e\$NCM\$OBh0lMWAG\$NG[Ns\$H\$7\$^\$7\$g\$&(B)

\$B\$3\$l\$rF@\$k\$K\$O8=:_!";DG0\$J;v\$K!"<+J,\$G%k!<%W\$r=q\$/I,MW\$,\$"\$j\$^\$9!#(B

\$B\$3\$N>u67\$r2~A1\$9\$k\$H\$\$\$&A*Br;h\$O\$"\$j\$^\$;\$s\$+!#\$D\$^\$j(BHash[ary].invert\$B\$,\$b\$&>/\$7(B
\$B8-\$/\$J\$l\$P\$\$\$\$\$s\$G\$9\$h\$M(B?

\$B0z?t\$N(B 1, 0 \$B\$,2?\$r0UL#\$9\$k\$+\$H\$\$\$&\$H!"(Benumerable \$B\$NMWAG\$+\$i(B
\$B%O%C%7%e\$N%-!<5Z\$SCM\$r<h\$j=P\$9;XDj\$G\$9!#(B
\$B6qBNE*\$K\$O(B ary \$B\$N3FMWAG\$KBP\$7!"(B[] \$B%a%=%C%I\$r8F\$S=P\$7!"(B
\$B\$=\$N0z?t\$K0z\$-EO\$7\$F%-!<(B/\$BCM\$rF@\$^\$9!#(B
\$B\$D\$^\$j!"(B[“matz”, “Yukihiro M.”][1] \$B\$H\$7\$F(B “Yukihiro M.”
\$B\$H\$\$\$&%-!<\$rF@\$F!"(B
[“matz”, “Yukihiro M.”][0] \$B\$H\$7\$F(B “matz” \$B\$H\$\$\$&CM\$rF@\$k\$o\$1\$G\$9!#(B

\$BJ,\$+\$j\$K\$/\$9\$.\$k\$H;W\$\$\$^\$9!#(B

\$B\$I\$&\$G\$7\$g\$&\$+!#(B

\$B\$3\$l\$,B8:_\$9\$k\$3\$H<+BN\$K\$OFC\$KH?BP\$7\$^\$;\$s\$,!"\$A\$g\$C\$HCj>]E*\$9\$.\$F5U\$K;H\$\$\$E\$i(B
\$B\$\$\$H46\$8\$^\$9!#:G=E\$K\$3\$N%"%k%4%j%:%`\$KMn\$A\$?\$H\$7\$F\$b!"%f!<%6!<\$K\$O\$b\$&>/\$7;H(B
\$B\$\$\$d\$9\$\$(BAPI\$B\$rDs6!\$9\$Y\$-\$G\$7\$g\$&!#\$?\$H\$(\$P?t\$r?t\$(\$k@lMQ\$N%a%=%C%I\$r:n\$k\$H\$+!#(B

# Index: hash.c

— hash.c (revision 29883)
+++ hash.c (working copy)
@@ -1706,6 +1706,17 @@
return ST_CONTINUE;
}

+static int
+rb_hash_invert_with_block_i(VALUE key, VALUE value, VALUE hash)
+{

• if (key == Qundef) return ST_CONTINUE;
• if (rb_hash_has_key(hash, value)) {
• key = rb_yield_values(3, value, rb_hash_aref(hash, value), key);
• }
• rb_hash_aset(hash, value, key);
• return ST_CONTINUE;
+}

/*

• call-seq:
• ``````hsh.invert -> new_hash
``````

@@ -1716,6 +1727,11 @@

• ``````h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
``````
• ``````h.invert   #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"}
``````
• Without a block given, entries with identical values are
• clobbered. No guarantee on which one shall be chosen. With a
• block on the other hand, you can choose which (or both). See
• Hash#update for how.
*/

static VALUE
@@ -1723,7 +1739,12 @@
{
VALUE h = rb_hash_new();

• rb_hash_foreach(hash, rb_hash_invert_i, h);
• if (rb_block_given_p()) {
• rb_hash_foreach(hash, rb_hash_invert_with_block_i, h);
• }
• else {
• rb_hash_foreach(hash, rb_hash_invert_i, h);
• }
return h;
}

(2010/11/27 19:51), Tanaka A. wrote:

\$B\$\$\$d!"\$b\$C\$H0lHLE*\$J;v\$r9M\$(\$F\$\$\$^\$9!#(B
CSV \$B\$+\$iFI\$_9~\$`!"\$H\$\$\$&>u67\$K?(\$l\$?\$N\$b\$=\$&\$J\$N\$G\$9\$,!"(B \$B0lHL\$K%U%#!<%k%I(B (\$B%+%i%`(B) \$B\$O\$U\$?\$D\$H\$O8B\$j\$^\$;\$s!#(B
\$B\$^\$?!“I,\$:%f%K!<%/\$J%-!<\$,\$”\$k\$H\$b8B\$j\$^\$;\$s!#(B

\$B\$=\$l\$b\$"\$C\$F!":G=i\$K%G!<%?\$,%O%C%7%e\$H\$7\$FB8:_\$9\$k!"(B
\$B\$H\$\$\$&2>Dj\$O\$7\$?\$/\$"\$j\$^\$;\$s!#(B

\$B%O%C%7%e\$G\$O\$J\$\$\$b\$N\$r(BHash\$B\$G07\$*\$&\$H\$\$\$&H/A[\$,\$=\$b\$=\$bGKC>\$7\$F\$^\$;\$s\$+(B

\$B0z?t\$N(B 1, 0 \$B\$,2?\$r0UL#\$9\$k\$+\$H\$\$\$&\$H!"(Benumerable \$B\$NMWAG\$+\$i(B
\$B%O%C%7%e\$N%-!<5Z\$SCM\$r<h\$j=P\$9;XDj\$G\$9!#(B
\$B6qBNE*\$K\$O(B ary \$B\$N3FMWAG\$KBP\$7!"(B[] \$B%a%=%C%I\$r8F\$S=P\$7!"(B
\$B\$=\$N0z?t\$K0z\$-EO\$7\$F%-!<(B/\$BCM\$rF@\$^\$9!#(B
\$B\$D\$^\$j!"(B[“matz”, “Yukihiro M.”][1] \$B\$H\$7\$F(B “Yukihiro M.”
\$B\$H\$\$\$&%-!<\$rF@\$F!"(B

[“matz”, “Yukihiro M.”][0] \$B\$H\$7\$F(B “matz”
\$B\$H\$\$\$&CM\$rF@\$k\$o\$1\$G\$9!#(B

\$BJ,\$+\$j\$K\$/\$9\$.\$k\$H;W\$\$\$^\$9!#(B

0, 1 \$B\$H\$\$\$&%\$%s%G%C%/%9\$,\$o\$+\$j\$K\$/\$\$!"\$H\$\$\$&0U?^\$G\$7\$g\$&\$+!)(B

\$B\$\$\$\$\$(!“0lHVL\$N0z?t\$,(Bkey\$B\$GFsHVL\$N0z?t\$,(Bvalue\$B\$G\$”\$k\$H\$\$\$&\$N\$,;zLL\$+\$iJ,\$+\$j\$E(B
\$B\$i\$\$\$H;W\$\$\$^\$9!#(B

\$B\$?\$H\$(\$P(Bary.categorize(0 =>
1)\$B\$H\$+=q\$\$\$F\$"\$l\$P;d\$K\$H\$C\$F\$OB?>/\$OJ,\$+\$j\$d\$9\$/\$J(B
\$B\$j\$^\$9!#\$"\$k\$\$\$O(Bary.categorize(key: 0, value: 1)\$B\$H\$+!#(B

\$B\$3\$l\$,B8:_\$9\$k\$3\$H<+BN\$K\$OFC\$KH?BP\$7\$^\$;\$s\$,!"\$A\$g\$C\$HCj>]E*\$9\$.\$F5U\$K;H\$\$\$E\$i(B

\$B\$\$\$H46\$8\$^\$9!#:G=E\$K\$3\$N%"%k%4%j%:%`\$KMn\$A\$?\$H\$7\$F\$b!"%f!<%6!<\$K\$O\$b\$&>/\$7;H(B

\$B\$\$\$d\$9\$\$(BAPI\$B\$rDs6!\$9\$Y\$-\$G\$7\$g\$&!#\$?\$H\$(\$P?t\$r?t\$(\$k@lMQ\$N%a%=%C%I\$r:n\$k\$H\$+!#(B

\$B\$R\$H\$D\$R\$H\$D?J\$a\$F\$\$\$3\$&\$+\$H;W\$C\$F\$\$\$F!"(B
\$BCM\$rG[Ns\$K\$9\$kI,MW\$,\$J\$\$>l9g\$H!"(B
\$B?t\$r?t\$(\$k\$b\$N\$K\$D\$\$\$F\$O!"(B
\$B\$^\$?8e\$G(B (\$B5\$\$,8~\$\$\$?\$H\$-\$K(B) \$B\$d\$m\$&\$+\$H;W\$C\$F\$\$\$^\$7\$?!#(B

\$B\$3\$l\$i\$K\$D\$\$\$F\$bH?BP\$G\$O\$"\$j\$^\$;\$s!#(B

2010\$BG/(B11\$B7n(B27\$BF|(B20:05 Urabe S. [email protected]:

\$B%O%C%7%e\$G\$O\$J\$\$\$b\$N\$r(BHash\$B\$G07\$*\$&\$H\$\$\$&H/A[\$,\$=\$b\$=\$bGKC>\$7\$F\$^\$;\$s\$+(B

\$BGKC>\$9\$k\$H\$\$\$&0UL#\$,@53N\$K\$OJ,\$+\$i\$J\$\$\$N\$G\$9\$,!"(B
\$B\$J\$K\$+\$rJ,N`\$9\$k\$H\$\$\$&\$N\$O%f%K!<%/\$J%-!<\$G\$J\$/\$F\$b2DG=\$G\$7\$g\$&(B?

\$B\$H\$/\$K!“(Bcategorize \$B\$O(B (\$B%G%U%)%k%H\$G\$O(B)
\$B%O%C%7%e\$NCM\$,G[Ns\$K\$J\$k\$?\$a!”(B
\$B%-!<\$,%f%K!<%/\$G\$“\$kI,MW\$b\$”\$j\$^\$;\$s\$7!“L5M}\$,\$”\$k\$b\$N\$H\$O;W\$C\$F\$\$\$^\$;\$s!#(B

0, 1 \$B\$H\$\$\$&%\$%s%G%C%/%9\$,\$o\$+\$j\$K\$/\$\$!"\$H\$\$\$&0U?^\$G\$7\$g\$&\$+!)(B

\$B\$\$\$\$\$(!“0lHVL\$N0z?t\$,(Bkey\$B\$GFsHVL\$N0z?t\$,(Bvalue\$B\$G\$”\$k\$H\$\$\$&\$N\$,;zLL\$+\$iJ,\$+\$j\$E(B

\$B\$i\$\$\$H;W\$\$\$^\$9!#(B

\$B\$?\$H\$(\$P(Bary.categorize(0 =>
1)\$B\$H\$+=q\$\$\$F\$“\$l\$P;d\$K\$H\$C\$F\$OB?>/\$OJ,\$+\$j\$d\$9\$/\$J(B
\$B\$j\$^\$9!#\$”\$k\$\$\$O(Bary.categorize(key: 0, value: 1)\$B\$H\$+!#(B

\$B\$J\$k\$[\$I!#(B

\$B%O%C%7%e\$N%-!<\$HCM\$rJB\$Y\$FI=\$9\$N\$O(B Hash#each_pair
\$B\$N%V%m%C%/0z?t\$H\$+!“(B
Hash[] \$B\$H\$+!”\$9\$G\$KDA\$7\$/\$J\$\$\$N\$G!“\$3\$NE@\$K\$D\$\$\$F!”;d\$O!"(B
\$B\$o\$+\$j\$K\$/\$\$\$H\$O;W\$C\$F\$\$\$^\$;\$s!#(B

2010\$BG/(B11\$B7n(B27\$BF|(B19:37 Urabe S. [email protected]:

\$B9,\$\$\$K\$7\$FBh0lMWAG\$+\$iBhFsMWAG\$X\$N%O%C%7%e\$O(B Hash[ary]
\$B\$G:n\$l\$k\$h\$&\$K\$J\$C\$?\$N\$G!“(B
\$B5U\$NBhFsMWAG\$+\$iBh0lMWAG\$X\$N%O%C%7%e\$K\$D\$\$\$F9M\$(\$^\$7\$g\$&!#(B
(\$BF1@+F1L>\$r9MN8\$7\$F!”%O%C%7%e\$NCM\$OBh0lMWAG\$NG[Ns\$H\$7\$^\$7\$g\$&(B)

\$B\$3\$l\$rF@\$k\$K\$O8=:_!“;DG0\$J;v\$K!”<+J,\$G%k!<%W\$r=q\$/I,MW\$,\$"\$j\$^\$9!#(B

\$B\$3\$N>u67\$r2~A1\$9\$k\$H\$\$\$&A*Br;h\$O\$"\$j\$^\$;\$s\$+!#\$D\$^\$j(BHash[ary].invert\$B\$,\$b\$&>/\$7(B

\$B8-\$/\$J\$l\$P\$\$\$\$\$s\$G\$9\$h\$M(B?

\$B\$\$\$d!"\$b\$C\$H0lHLE*\$J;v\$r9M\$(\$F\$\$\$^\$9!#(B
CSV \$B\$+\$iFI\$_9~\$`!"\$H\$\$\$&>u67\$K?(\$l\$?\$N\$b\$=\$&\$J\$N\$G\$9\$,!"(B \$B0lHL\$K%U%#!<%k%I(B (\$B%+%i%`(B) \$B\$O\$U\$?\$D\$H\$O8B\$j\$^\$;\$s!#(B
\$B\$^\$?!“I,\$:%f%K!<%/\$J%-!<\$,\$”\$k\$H\$b8B\$j\$^\$;\$s!#(B

\$B\$=\$l\$b\$“\$C\$F!”:G=i\$K%G!<%?\$,%O%C%7%e\$H\$7\$FB8:_\$9\$k!“(B
\$B\$H\$\$\$&2>Dj\$O\$7\$?\$/\$”\$j\$^\$;\$s!#(B

\$B0z?t\$N(B 1, 0 \$B\$,2?\$r0UL#\$9\$k\$+\$H\$\$\$&\$H!“(Benumerable \$B\$NMWAG\$+\$i(B
\$B%O%C%7%e\$N%-!<5Z\$SCM\$r<h\$j=P\$9;XDj\$G\$9!#(B
\$B6qBNE*\$K\$O(B ary \$B\$N3FMWAG\$KBP\$7!”(B[] \$B%a%=%C%I\$r8F\$S=P\$7!“(B
\$B\$=\$N0z?t\$K0z\$-EO\$7\$F%-!<(B/\$BCM\$rF@\$^\$9!#(B
\$B\$D\$^\$j!”(B[“matz”, “Yukihiro M.”][1] \$B\$H\$7\$F(B “Yukihiro M.”
\$B\$H\$\$\$&%-!<\$rF@\$F!"(B
[“matz”, “Yukihiro M.”][0] \$B\$H\$7\$F(B “matz”
\$B\$H\$\$\$&CM\$rF@\$k\$o\$1\$G\$9!#(B

\$BJ,\$+\$j\$K\$/\$9\$.\$k\$H;W\$\$\$^\$9!#(B

0, 1 \$B\$H\$\$\$&%\$%s%G%C%/%9\$,\$o\$+\$j\$K\$/\$\$!"\$H\$\$\$&0U?^\$G\$7\$g\$&\$+!)(B

\$B\$3\$l\$,B8:_\$9\$k\$3\$H<+BN\$K\$OFC\$KH?BP\$7\$^\$;\$s\$,!"\$A\$g\$C\$HCj>]E*\$9\$.\$F5U\$K;H\$\$\$E\$i(B

\$B\$\$\$H46\$8\$^\$9!#:G=E\$K\$3\$N%“%k%4%j%:%`\$KMn\$A\$?\$H\$7\$F\$b!”%f!<%6!<\$K\$O\$b\$&>/\$7;H(B

\$B\$\$\$d\$9\$\$(BAPI\$B\$rDs6!\$9\$Y\$-\$G\$7\$g\$&!#\$?\$H\$(\$P?t\$r?t\$(\$k@lMQ\$N%a%=%C%I\$r:n\$k\$H\$+!#(B

\$B\$R\$H\$D\$R\$H\$D?J\$a\$F\$\$\$3\$&\$+\$H;W\$C\$F\$\$\$F!“(B
\$BCM\$rG[Ns\$K\$9\$kI,MW\$,\$J\$\$>l9g\$H!”(B
\$B?t\$r?t\$(\$k\$b\$N\$K\$D\$\$\$F\$O!"(B
\$B\$^\$?8e\$G(B (\$B5\$\$,8~\$\$\$?\$H\$-\$K(B) \$B\$d\$m\$&\$+\$H;W\$C\$F\$\$\$^\$7\$?!#(B

module Enumerable

… } → hash

enum.

element.

nested.

hashes.

# By default, this method assumes the key selectors categorizes

elements in enum uniquely.

# If the key selectors generates same keys for two or more elements,

ArgumentError is raised.

:price => 100},

:price => 300},

# {:fruit => “grapefruit”, :color => “yellow”, :taste =>

“tart”, :price => 200}]

category.

vsel.

for the seed.

# #=> {“yellow”=>300, “green”=>300}

def unique_categorize(args, &update_proc)
opts = args.last.kind_of?(Hash) ? args.pop.dup : {}
if update_proc
opts[:update] = lambda {|ks, s, v| update_proc.call(s, v) }
else
seed = Object.new
opts[:seed] = seed
opts[:update] = lambda {|ks, s, v|
if s.equal? seed
v
else
raise ArgumentError, “ambiguous key: #{ks.map {|k| k.inspect
}.join(‘,’)}”
end
}
end
categorize(
(args + [opts]))
end

selectors.

:price => 100},

:price => 300},

# {:fruit => “grapefruit”, :color => “yellow”, :taste =>

“tart”, :price => 200}]

enum.

# See Enumerable#categorize for details of selectors.

def category_count(args)
categorize(
(args + [lambda {|e| 1 }, {:update => lambda {|ks, s,
v| s + v }}]))
end

end

2010\$BG/(B11\$B7n(B27\$BF|(B23:19 Yukihiro M. [email protected]:

Enumerable\$B\$+\$i(Bhash\$B\$r@8@.\$9\$k%a%=%C%I\$O\$A\$g\$&\$I:rF|M_\$7\$\$\$H;W\$C(B
\$B\$?\$P\$+\$j\$J\$N\$G!“\$=\$&\$\$\$&%a%=%C%I\$NI,MW@-\$OM}2r\$7\$^\$9!#\$,!”(B
categorize \$B\$h\$j\$b@h\$K!VMWAG\$r%V%m%C%/\$KEO\$7\$F!"\$=\$N%V%m%C%/\$N(B
\$B@[email protected]\$K\$h\$C\$F%O%C%7%e\$r:n\$k%a%=%C%I!W\$,M_\$7\$\$\$G\$9!#(B

\$B\$^\$!!"%O%C%7%e\$r@8@.\$9\$k%a%=%C%I\$OG[Ns\$r@8@.\$9\$k%a%=%C%I\$K\$/\$i\$Y\$F(B
\$B\$:\$\$\$V\$s\$H>/\$J\$\$\$G\$9\$h\$M!#(B

#=> {“Yukihiro M.”=>[“matz”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],

\$B\$G\$9!#\$^\$@(Bxxx\$B\$K3:Ev\$9\$kE,@Z\$JL>A0\$r;W\$\$\$D\$\$\$F\$\$\$^\$;\$s\$,!#\$3\$l(B
\$B\$O(B(\$B%G%U%)%k%H\$G\$O(B)Enumerable\$B\$NMWAG\$,G[Ns\$G\$"\$k\$3\$H\$r2>Dj\$7\$F(B
\$B\$\$\$k(B categorize \$B\$h\$j\$bHFMQE*\$G\$O\$J\$\$\$+\$H;W\$\$\$^\$9!#(B

\$B3NG’\$7\$?\$\$\$N\$G\$9\$,!"%O%C%7%e\$NCM\$OG[Ns\$K\$J\$k\$N\$G\$9\$M(B?

\$BG[Ns\$G\$J\$/!“:G8e\$NCM\$J\$i!”(BHash[ary.map {|e| [e[1], e[0]] }]
\$B\$HEy2A\$@\$H(B
\$B;W\$&\$N\$G!#(B

\$B\$G!"\$3\$N%a%=%C%I\$NE,@Z\$JL>A0\$O\$J\$s\$G\$7\$g\$&\$M!#(Bmake_map\$B\$H\$+9M(B
\$B\$(\$?\$N\$G\$9\$,!"H~\$7\$/\$J\$\$!#(BLisp\$B\$H\$+\$K;w\$?\$h\$&\$J4X?t\$J\$+\$C\$?\$C(B
\$B\$1\$J!#(B

hashmap \$B\$H\$\$\$&\$N\$,0lHV\$K;W\$\$Ib\$+\$S\$^\$7\$?\$,!"(B
\$B%O%C%7%e\$NCM\$,G[Ns\$H\$9\$k\$H0c\$&\$J\$!!#(B

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42638] Enumerable#categorize”
on Sat, 27 Nov 2010 18:45:03 +0900, Tanaka A. [email protected]
writes:

|enumerable \$B\$+\$i(B hash \$B\$r@8@.\$9\$k%a%=%C%I\$H\$7\$F(B
|Enumerable#categorize \$B\$rDI2C\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!#(B

Enumerable\$B\$+\$i(Bhash\$B\$r@8@.\$9\$k%a%=%C%I\$O\$A\$g\$&\$I:rF|M_\$7\$\$\$H;W\$C(B
\$B\$?\$P\$+\$j\$J\$N\$G!“\$=\$&\$\$\$&%a%=%C%I\$NI,MW@-\$OM}2r\$7\$^\$9!#\$,!”(B
categorize \$B\$h\$j\$b@h\$K!VMWAG\$r%V%m%C%/\$KEO\$7\$F!"\$=\$N%V%m%C%/\$N(B
\$B@[email protected]\$K\$h\$C\$F%O%C%7%e\$r:n\$k%a%=%C%I!W\$,M_\$7\$\$\$G\$9!#(B

\$B\$D\$^\$j!"(B

| ary = [
| [“matz”, “Yukihiro M.”],
| [“akr”, “Tanaka A.”],
| [“usa”, “Usaku NAKAMURA”],
| [“naruse”, “NARUSE, Yui”],
| ]

h = ary.xxx{|e| [e[1],e[0]]}
#=> {“Yukihiro M.”=>[“matz”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],

\$B\$G\$9!#\$^\$@(Bxxx\$B\$K3:Ev\$9\$kE,@Z\$JL>A0\$r;W\$\$\$D\$\$\$F\$\$\$^\$;\$s\$,!#\$3\$l(B
\$B\$O(B(\$B%G%U%)%k%H\$G\$O(B)Enumerable\$B\$NMWAG\$,G[Ns\$G\$"\$k\$3\$H\$r2>Dj\$7\$F(B
\$B\$\$\$k(B categorize \$B\$h\$j\$bHFMQE*\$G\$O\$J\$\$\$+\$H;W\$\$\$^\$9!#(B

\$B\$\$\$d!“(B categorize \$B\$OITMW\$@\$H\$+\$^\$G\$O<gD%\$7\$^\$;\$s\$,!”\$3\$C\$A\$,(B
\$B@h\$KM_\$7\$\$\$G\$9!#(B

|* \$B%M%9%H\$7\$?%O%C%7%e\$N@8@.(B
|* \$B%O%C%7%e\$NCM\$N8e=hM}(B
|* :seed, :op, :update \$B%%W%7%g%s(B
|
\$BJ#?t\$NCM\$r<h\$j=P\$9(B

\$B;d\$NDs0F\$G\$O\$3\$NJU\$OBP1~\$G\$-\$J\$\$\$N\$G!“(B categorize \$B\$N0UL#\$O\$”(B
\$B\$k\$H\$O;W\$\$\$^\$9!#(B

\$B\$G!"\$3\$N%a%=%C%I\$NE,@Z\$JL>A0\$O\$J\$s\$G\$7\$g\$&\$M!#(Bmake_map\$B\$H\$+9M(B
\$B\$(\$?\$N\$G\$9\$,!"H~\$7\$/\$J\$\$!#(BLisp\$B\$H\$+\$K;w\$?\$h\$&\$J4X?t\$J\$+\$C\$?\$C(B
\$B\$1\$J!#(B

``````                            \$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m(B /:|)
``````

2010\$BG/(B11\$B7n(B27\$BF|(B23:51 Yukihiro M. [email protected]:

|\$B3NG’\$7\$?\$\$\$N\$G\$9\$,!"%O%C%7%e\$NCM\$OG[Ns\$K\$J\$k\$N\$G\$9\$M(B?

\$B\$"!"0c\$\$\$^\$9!#\$5\$C\$-\$N\$^\$^\$G\$OG[Ns\$K\$O\$J\$j\$^\$;\$s!#G[Ns\$K\$9\$k(B
\$B\$?\$a\$K\$O(B

h = ary.xxx{|e| [e[1],[e[0]]]}

\$B\$G\$9\$M!#(B

\$B\$H\$9\$k\$H!“\$b\$A\$m\$s!”>e5-\$N\$h\$&\$KG[Ns\$K\$7\$?\$H\$-\$K\$OF1\$8%-!<\$NCM\$r(B
\$B=8\$a\$FJ,N`\$7\$F\$/\$l\$?\$j\$O\$7\$J\$\$\$o\$1\$G\$9\$h\$M!#(B

categorize \$B\$O\$=\$l\$r\$d\$C\$F\$/\$l\$k\$b\$N\$J\$N\$G!#(B

|\$BG[Ns\$G\$J\$/!“:G8e\$NCM\$J\$i!”(BHash[ary.map {|e| [e[1], e[0]] }] \$B\$HEy2A\$@\$H(B
|\$B;W\$&\$N\$G!#(B

\$B\$^\$"!“0UL#E*\$K\$OEy2A\$J\$s\$G\$9\$,!”(B

• \$B\$b\$C\$H%9%H%l!<%H\$K=q\$-\$?\$\$(B
• \$B\$G\$-\$l\$PCf4VG[Ns\$O:n\$j\$?\$/\$J\$\$(B

\$B\$H\$\$\$&E@\$GFHN)\$7\$?%a%=%C%I\$K\$7\$?\$\$\$G\$9!#(B

\$B\$J\$k\$[\$I!#(B

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42644] Re: Enumerable#categorize”
on Sat, 27 Nov 2010 23:35:51 +0900, Tanaka A. [email protected]
writes:

|\$B3NG’\$7\$?\$\$\$N\$G\$9\$,!"%O%C%7%e\$NCM\$OG[Ns\$K\$J\$k\$N\$G\$9\$M(B?

\$B\$"!"0c\$\$\$^\$9!#\$5\$C\$-\$N\$^\$^\$G\$OG[Ns\$K\$O\$J\$j\$^\$;\$s!#G[Ns\$K\$9\$k(B
\$B\$?\$a\$K\$O(B

h = ary.xxx{|e| [e[1],[e[0]]]}

\$B\$G\$9\$M!#(B

|\$BG[Ns\$G\$J\$/!“:G8e\$NCM\$J\$i!”(BHash[ary.map {|e| [e[1], e[0]] }] \$B\$HEy2A\$@\$H(B
|\$B;W\$&\$N\$G!#(B

\$B\$^\$"!“0UL#E*\$K\$OEy2A\$J\$s\$G\$9\$,!”(B

• \$B\$b\$C\$H%9%H%l!<%H\$K=q\$-\$?\$\$(B
• \$B\$G\$-\$l\$PCf4VG[Ns\$O:n\$j\$?\$/\$J\$\$(B

\$B\$H\$\$\$&E@\$GFHN)\$7\$?%a%=%C%I\$K\$7\$?\$\$\$G\$9!#(B

|> \$B\$G!"\$3\$N%a%=%C%I\$NE,@Z\$JL>A0\$O\$J\$s\$G\$7\$g\$&\$M!#(Bmake_map\$B\$H\$+9M(B
|> \$B\$(\$?\$N\$G\$9\$,!“H~\$7\$/\$J\$\$!#(BLisp\$B\$H\$+\$K;w\$?\$h\$&\$J4X?t\$J\$+\$C\$?\$C(B
|> \$B\$1\$J!#(B
|
|hashmap \$B\$H\$\$\$&\$N\$,0lHV\$K;W\$\$Ib\$+\$S\$^\$7\$?\$,!”(B
|\$B%O%C%7%e\$NCM\$,G[Ns\$H\$9\$k\$H0c\$&\$J\$!!#(B

\$BG[Ns\$G\$O\$J\$\$\$N\$G(B hashmap \$B\$OE,@Z\$J\$N\$+\$b\$7\$l\$^\$;\$s!#(B

``````                            \$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m(B /:|)
``````

\$B!!\$5\$5\$@\$G\$9!#(B

(2010/11/27 23:19), Yukihiro M. wrote:

h = ary.xxx{|e| [e[1],e[0]]}
#=> {“Yukihiro M.”=>[“matz”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],

\$B!!\$3\$N(B xxx \$B\$K\$D\$\$\$F!"(Bjust idea \$B\$J\$s\$G\$9\$,!"(B

• Enumerable#map \$B\$KBh0l0z?t\$rEO\$;\$k\$h\$&\$K\$9\$k(B
• \$BBh0l0z?t\$K(B << \$B\$7\$F\$\$\$/(B
• \$B>JN,\$5\$l\$l\$P(B []
• Hash#<< \$B\$r:n\$k(B

\$B\$C\$F\$N\$O\$I\$&\$G\$7\$g\$&\$+!#K\Ev\$K;W\$\$\$D\$-\$J\$N\$G!"\$-\$C\$HBLL\$J\$H\$3\$m\$,\$?\$/\$5(B
\$B\$s\$"\$j\$=\$&\$G\$9\$,!#(B

class Hash
def <<(arg)
k, v = *arg
self[k] = v
end
end

module Enumerable
def map container = []
self.each{|e|
container << yield(e)
}
container
end
end

class Array
remove_method :map
end

ary = [
[“matz”, “Yukihiro M.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],
[“naruse”, “NARUSE, Yui”],
]

p ary.map{|e|
[e[0], e[1]]
}

# Hash \$B\$,:n\$l\$k(B

p ary.map({}){|e|
[e[0], e[1]]
}

# \$B4{B8\$N%G!<%?\$bEO\$;\$k(B

p ary.map({‘znz’ => ‘Kazuhiro NISHIYAMA’}){|e|
[e[0], e[1]]
}

2010\$BG/(B11\$B7n(B27\$BF|(B23:19 Yukihiro M. [email protected]:

|* \$B%M%9%H\$7\$?%O%C%7%e\$N@8@.(B
|* \$B%O%C%7%e\$NCM\$N8e=hM}(B
|* :seed, :op, :update \$B%%W%7%g%s(B
|
\$BJ#?t\$NCM\$r<h\$j=P\$9(B

\$B;d\$NDs0F\$G\$O\$3\$NJU\$OBP1~\$G\$-\$J\$\$\$N\$G!“(B categorize \$B\$N0UL#\$O\$”(B
\$B\$k\$H\$O;W\$\$\$^\$9!#(B

\$B%V%m%C%/\$,JV\$7\$?G[Ns\$ND9\$5\$,(B
3\$B0J>e\$J\$i%M%9%H\$7\$?%O%C%7%e\$r@8@.\$9\$k\$H\$\$\$&(B
\$B\$d\$j\$+\$?\$O\$“\$k\$+\$b\$7\$l\$^\$;\$s!#(B
\$B%V%m%C%/\$,JV\$9G[Ns\$ND9\$5\$,JQ2=\$7\$?\$j\$9\$k\$H%(%i!<\$,H/@8\$7\$F\$7\$^\$&\$3\$H\$b(B
\$B\$”\$k\$@\$m\$&!“\$H\$\$\$&E@\$O\$”\$j\$^\$9\$,!#(B

\$BJ#?t\$NCM\$r<h\$j=P\$9\$N\$O!“%V%m%C%/\$,JV\$9G[Ns\$NCMItJ,\$rE,Ev\$K:n\$l\$P\$\$\$\$\$N\$G!”(B
\$B\$3\$l\$OLdBj\$J\$/<B8=\$G\$-\$k\$H;W\$\$\$^\$9!#(B

\$B;D\$j\$N\$U\$?\$D\$O\$^\$:F1\$8%-!<\$,J#?t2s@8@.\$5\$l\$?\$H\$-\$K\$I\$&\$9\$k\$+!“(B
\$B\$H\$\$\$&E@\$,\$”\$C\$F!“\$^\$D\$b\$H\$5\$s\$N\$O=E\$M=q\$-\$7\$F\$\$\$C\$F:G8e\$N\$,;D\$k!”(B
\$B\$H\$\$\$&\$N\$,A[Dj\$N\$h\$&\$G\$9!#(B
\$B0l1~!“(B:seed, :op, :update \$B%*%W%7%g%s\$rF3F~\$7\$F\$=\$3\$r(B
\$B%+%9%?%^%\$%:\$G\$-\$k\$h\$&\$K\$9\$k!”\$H\$\$\$&<j\$b\$J\$/\$O\$“\$j\$^\$;\$s!#(B
\$B\$?\$@!”%V%m%C%/\$,F0\$\$\$?8e\$K(B :op \$B\$d(B :update
\$B\$,F0\$/\$H\$\$\$&<B9T=g=x\$K\$J\$k\$N\$G(B
\$B:8\$+\$i1&\$KF0\$\$\$F\$\$\$/\$H\$\$\$&46\$8\$K\$J\$j\$^\$;\$s\$,!#(B

\$B\$“\$H!“J?6Q\$r5a\$a\$k\$H\$-\$J\$I\$K\$O!”:G8e\$KFHN)\$7\$?8e=hM}(B
(\$BJ?6Q\$N>l9g\$K\$O3d\$j;;(B)
\$B\$,I,MW\$G!”\$3\$l\$O\$^\$!!"\$I\$&\$7\$F\$b\$\$\$l\$k\$J\$i\$^\$?JL\$N(B
\$B%*%W%7%g%s\$G\$9\$+\$M\$'!#(B

\$B\$^\$!!“%*%W%7%g%s\$O\$d\$j\$9\$.\$@\$H\$9\$l\$P!”\$U\$?\$D\$O\$I\$&\$K\$+\$J\$k!“(B
\$B\$H\$\$\$&\$”\$?\$j\$G\$7\$g\$&\$+!#(B

\$B\$?\$k\$\$\$G\$9!#(B

\$BOC\$,K\Bj\$H\$:\$l\$F\$-\$F\$F!“(B
\$BJL%9%l%C%I\$r:n\$k\$Y\$-\$+\$A\$g\$C\$H\$J\$d\$_\$^\$7\$?\$,!”(B

``````   "SASADA Koichi"=>["ko1"]}
``````

\$B!!\$3\$N(B xxx \$B\$K\$D\$\$\$F!“(Bjust idea \$B\$J\$s\$G\$9\$,!”(B

• Enumerable#map \$B\$KBh0l0z?t\$rEO\$;\$k\$h\$&\$K\$9\$k(B
• \$BBh0l0z?t\$K(B << \$B\$7\$F\$\$\$/(B
• \$B>JN,\$5\$l\$l\$P(B []
• Hash#<< \$B\$r:n\$k(B

\$B\$C\$F\$N\$O\$I\$&\$G\$7\$g\$&\$+!#K\Ev\$K;W\$\$\$D\$-\$J\$N\$G!“\$-\$C\$HBLL\$J\$H\$3\$m\$,\$?\$/\$5(B
\$B\$s\$”\$j\$=\$&\$G\$9\$,!#(B

inject\$B\$NN`;w\$G!";}\$A\$^\$o\$9\$N\$,%V%m%C%/\$N7k2L\$G\$J\$/Bh0l0z?t\$H\$\$\$&%a%=%C%I\$rMQ0U\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!)(B

module Enumerable

def apply_to target
self.each{|e|
yield(target,e)
}
target

end
end

ary = [
[“matz”, “Yukihiro M.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],

[“naruse”, “NARUSE, Yui”],
]

p ary.apply_to({}){|r,e|
r[e[1]] = e[0]
}

“Tanaka
Akira”=>“akr”, “Usaku NAKAMURA”=>“usa”, “NARUSE, Yui” =>“naruse”,
\$B\$,F@\$i\$l\$^\$9!#(B

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42646] Re: Enumerable#categorize”
on Sat, 27 Nov 2010 23:59:00 +0900, Tanaka A. [email protected]
writes:
|
|2010\$BG/(B11\$B7n(B27\$BF|(B23:51 Yukihiro M. [email protected]:

|> \$B\$“!“0c\$\$\$^\$9!#\$5\$C\$-\$N\$^\$^\$G\$OG[Ns\$K\$O\$J\$j\$^\$;\$s!#G[Ns\$K\$9\$k(B
|> \$B\$?\$a\$K\$O(B
|>
|> h = ary.xxx{|e| [e[1],[e[0]]]}
|>
|> \$B\$G\$9\$M!#(B
|
|\$B\$H\$9\$k\$H!”\$b\$A\$m\$s!”>e5-\$N\$h\$&\$KG[Ns\$K\$7\$?\$H\$-\$K\$OF1\$8%-!<\$NCM\$r(B
|\$B=8\$a\$FJ,N`\$7\$F\$/\$l\$?\$j\$O\$7\$J\$\$\$o\$1\$G\$9\$h\$M!#(B
|
|categorize \$B\$O\$=\$l\$r\$d\$C\$F\$/\$l\$k\$b\$N\$J\$N\$G!#(B

\$B\$=\$&\$G\$9\$M!#\$I\$A\$i\$bM_\$7\$\$%1!<%9\$O\$“\$k\$N\$G\$9\$,!”\$I\$&\$9\$k\$N\$,(B
\$B:GE,\$+\$K\$D\$\$\$F\$O\$^\$@7kO@\$,=P\$F\$\$\$^\$;\$s!#%*%W%7%g%s\$H\$\$\$&\$N\$b(B
\$B\$R\$H\$D\$N%“%\$%G%#%”\$G\$7\$g\$&\$7!"JLL>\$N%a%=%C%I\$NJ}\$,NI\$\$\$N\$+\$b(B
\$B\$7\$l\$^\$;\$s!#(B

\$B\$9\$_\$^\$;\$s!"85!9\$N(B categorize \$B\$N%9%l%C%I\$rC%\$C\$F\$7\$^\$C\$F!#(B

\$BL5M}LpM}!“K\Bj\$N(B categorize \$B\$KLa\$9\$H!“8D?ME*\$K\$OEDCf\$5\$s\$,Ds(B
\$B<(\$5\$l\$?;EMM\$O!“8DJL\$K\$O;H\$(\$k6ILL\$,\$”\$k\$N\$OJ,\$+\$k\$N\$G\$9\$,!”(B
\$B\$R\$H\$D\$N%a%=%C%I\$H\$7\$F\$O9b5!G=\$9\$.\$k\$h\$&\$J0u>]\$,\$”\$j\$^\$9!#\$?(B
\$B\$H\$(\$P!“\$5\$-\$[\$I\$N;d\$N(B xxx \$B\$,F1\$8%-!<\$r=8\$a\$FJ,N`\$9\$k\$H\$7\$F!”(B
\$B\$=\$N%a%=%C%I\$K(B categorize \$B\$H\$\$\$&L>A0\$rIU\$1\$k\$H\$9\$k\$H!";d9%\$_(B
\$B\$N%G%6%\$%s\$J\$N\$G\$9\$,!#(B

``````                            \$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m(B /:|)
``````

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42649] Re: Enumerable#categorize”
on Sun, 28 Nov 2010 00:26:30 +0900, Masaya TARUI [email protected]
writes:

|> \$B!!\$3\$N(B xxx \$B\$K\$D\$\$\$F!“(Bjust idea \$B\$J\$s\$G\$9\$,!”(B
|>
|> - Enumerable#map \$B\$KBh0l0z?t\$rEO\$;\$k\$h\$&\$K\$9\$k(B
|> - \$BBh0l0z?t\$K(B << \$B\$7\$F\$\$\$/(B
|> - \$B>JN,\$5\$l\$l\$P(B []
|> - Hash#<< \$B\$r:n\$k(B
|>
|> \$B\$C\$F\$N\$O\$I\$&\$G\$7\$g\$&\$+!#K\Ev\$K;W\$\$\$D\$-\$J\$N\$G!“\$-\$C\$HBLL\$J\$H\$3\$m\$,\$?\$/\$5(B
|> \$B\$s\$”\$j\$=\$&\$G\$9\$,!#(B
|
|inject\$B\$NN`;w\$G!";}\$A\$^\$o\$9\$N\$,%V%m%C%/\$N7k2L\$G\$J\$/Bh0l0z?t\$H\$\$\$&%a%=%C%I\$rMQ0U\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!)(B

\$B\$I\$A\$i\$N%“%\$%G%#%”\$b6=L#?<\$\$\$G\$9(B(apply_to\$B\$O\$“\$^\$j9%\$-\$JL>A0\$G(B
\$B\$O\$J\$\$\$G\$9\$,!K!#\$3\$l\$i\$O\$”\$^\$j9M\$(\$J\$\$\$G:n\$k\$H%-!<\$,=EJ#\$9\$k(B
\$B\$HCM\$r=8\$a\$J\$\$\$G>e=q\$-\$7\$^\$9\$h\$M!#\$=\$3\$r\$&\$^\$/=hM}\$G\$-\$k\$H\$h(B
\$B\$\$\$N\$G\$9\$,!#(B

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42654] Re: Enumerable#categorize”
on Sun, 28 Nov 2010 23:57:23 +0900, Tanaka A. [email protected]
writes:

|categorize \$B\$H(B hashmap \$B\$r;n\$7\$K:n\$C\$F\$_\$k\$H!“\$3\$s\$J\$+\$s\$8\$G\$9\$+\$M!#(B
|\$B%*%W%7%g%s\$r\$\$\$8\$k\$H\$I\$C\$A\$b<B8=\$G\$-\$k\$N\$G!”\$I\$C\$A\$r4pHW\$K\$7\$F\$b\$\$\$\$\$s\$G\$9\$,!“(B
|\$B\$3\$3\$G\$O(B categorize \$B\$r4pHW\$K\$7\$F!”(Bhashmap \$B\$O(B categorize \$B\$r;H\$C\$F(B
|\$B<BAu\$7\$F\$"\$j\$^\$9!#(B

\$B5sF0\$H\$7\$F\$OBgJQ9%\$\$G\$9!#\$?\$@\$7!“(B categorize \$B\$O\$H\$b\$+\$/!”(B
hashmap \$B\$O\$\$\$^\$\$\$AL>A0\$K<+?.\$,;}\$F\$J\$\$\$N\$G!"99\$J\$kD4::(B(\$B4{B8(B
\$B\$N8@8l\$KN`;w5!G=\$O\$J\$\$\$+(B)\$B\$,I,MW\$+\$b\$7\$l\$^\$;\$s!#\$^\$?!"B>\$NJ}(B
\$B\$N\$40U8+\$b4?7^\$7\$^\$9!#(Bruby-core\$B\$K\$bJ9\$\$\$F\$
\$k\$Y\$-\$+\$J!#(B

2010\$BG/(B11\$B7n(B28\$BF|(B0:46 Yukihiro M. [email protected]:

\$BL5M}LpM}!“K\Bj\$N(B categorize \$B\$KLa\$9\$H!“8D?ME*\$K\$OEDCf\$5\$s\$,Ds(B
\$B<(\$5\$l\$?;EMM\$O!“8DJL\$K\$O;H\$(\$k6ILL\$,\$”\$k\$N\$OJ,\$+\$k\$N\$G\$9\$,!”(B
\$B\$R\$H\$D\$N%a%=%C%I\$H\$7\$F\$O9b5!G=\$9\$.\$k\$h\$&\$J0u>]\$,\$”\$j\$^\$9!#\$?(B
\$B\$H\$(\$P!“\$5\$-\$[\$I\$N;d\$N(B xxx \$B\$,F1\$8%-!<\$r=8\$a\$FJ,N`\$9\$k\$H\$7\$F!”(B
\$B\$=\$N%a%=%C%I\$K(B categorize \$B\$H\$\$\$&L>A0\$rIU\$1\$k\$H\$9\$k\$H!";d9%\$_(B
\$B\$N%G%6%\$%s\$J\$N\$G\$9\$,!#(B

categorize \$B\$H(B hashmap
\$B\$r;n\$7\$K:n\$C\$F\$_\$k\$H!“\$3\$s\$J\$+\$s\$8\$G\$9\$+\$M!#(B
\$B%*%W%7%g%s\$r\$\$\$8\$k\$H\$I\$C\$A\$b<B8=\$G\$-\$k\$N\$G!”\$I\$C\$A\$r4pHW\$K\$7\$F\$b\$\$\$\$\$s\$G\$9\$,!“(B
\$B\$3\$3\$G\$O(B categorize \$B\$r4pHW\$K\$7\$F!”(Bhashmap \$B\$O(B categorize
\$B\$r;H\$C\$F(B
\$B<BAu\$7\$F\$"\$j\$^\$9!#(B

\$B;d\$N:G=i\$N0F\$HHf3S\$9\$k\$H!“(B
\$B5!G=E*\$K\$O8e=hM}\$rF~\$l\$i\$l\$J\$\$\$H\$\$\$&E@\$,0c\$\$\$G\$7\$g\$&\$+!#(B
\$B\$3\$l\$O%V%m%C%/\$,JV\$9G[Ns\$ND9\$5\$,0lDj\$H\$O8B\$i\$J\$\$\$?\$a!”(B
\$B%O%C%7%e\$r:n\$j=*\$(\$?8e!"\$I\$3\$,CM\$J\$N\$+\$o\$+\$i\$J\$\$\$+\$i\$G\$9!#(B
\$B7k2L\$H\$7\$F!“3F%+%F%4%jKh\$KJ?6Q\$r5a\$a\$k\$J\$I!“8e=hM}\$,I,MW\$J\$b\$N\$O(B
\$B0l2s\$G\$O\$G\$-\$J\$/\$F!”(Bcategorize \$B\$7\$?\$”\$H\$K(B hashmap
\$B\$9\$k\$H\$+\$K\$J\$k\$N\$G\$7\$g\$&!#(B

\$B\$?\$@\$3\$l\$O!“Cm0U?<\$/\$d\$l\$P!”;d\$N0F\$H0c\$C\$F!“>l=j\$K\$h\$C\$F%M%9%H\$N?<\$5\$,(B
\$B0[\$J\$k%O%C%7%e\$r@8@.\$G\$-\$k\$H\$\$\$&\$3\$H\$G\$b\$”\$j\$^\$9!#(B

a = %w[alpaca alpha alphabet alpine alpine]
pp a.categorize(:op=>:+) {|s| s.split(//) + [:count, 1] }
#=>
{“a”=>
{“l”=>
{“p”=>
{“a”=>{“c”=>{“a”=>{:count=>1}}},
“h”=>{“a”=>{:count=>1, “b”=>{“e”=>{“t”=>{:count=>1}}}}},
“i”=>{“n”=>{“e”=>{:count=>2}}}}}}}

\$B\$&\$s\$^\$!\$3\$l\$O\$3\$l\$G\$\$\$\$\$h\$&\$J5\$\$,\$7\$^\$9!#(B

module Enumerable

“tart”}]

seed.

follows.

# :op => lambda {|s, v| !s ? [v] : (s << v) }

def categorize(opts={})
if opts.include? :seed
has_seed = true
seed = opts[:seed]
else
has_seed = false
seed = nil
end
if opts.include?(:update) && opts.include?(:op)
raise ArgumentError, “both :update and :op specified”
elsif opts.include?(:update)
update = opts[:update].to_proc
elsif opts.include?(:op)
op = opts[:op].to_proc
update = lambda {|ks, s, v| op.call(s, v) }
else
update = lambda {|ks, s, v| !s ? [v] : (s << v) }
if !has_seed
has_seed = true
seed = nil
end
end
result = {}
self.each {|elt|
kv = yield(elt)
kv = kv.to_ary
h = result
0.upto(kv.length-3) {|i|
k = kv[i]
h = (h[k] ||= {})
}
lastk = kv[-2]
v = kv[-1]
if !h.include?(lastk)
if !has_seed
h[lastk] = v
else
h[lastk] = update.call(kv[0…-1], seed, v)
end
else
h[lastk] = update.call(kv[0…-1], h[lastk], v)
end
}
result
end

# enum.hashmap([opts]) {|elt| [key1, …, val] } → hash

def hashmap(opts={}, &block)
opts = Hash[opts]
opts[:update] ||= lambda {|ks, x, y| y }
categorize(opts, &block)
end
end

2010\$BG/(B11\$B7n(B29\$BF|(B13:56 Yukihiro M. [email protected]:

\$B5sF0\$H\$7\$F\$OBgJQ9%\$\$G\$9!#\$?\$@\$7!“(B categorize \$B\$O\$H\$b\$+\$/!”(B
hashmap \$B\$O\$\$\$^\$\$\$AL>A0\$K<+?.\$,;}\$F\$J\$\$\$N\$G!"99\$J\$kD4::(B(\$B4{B8(B
\$B\$N8@8l\$KN`;w5!G=\$O\$J\$\$\$+(B)\$B\$,I,MW\$+\$b\$7\$l\$^\$;\$s!#\$^\$?!"B>\$NJ}(B
\$B\$N\$40U8+\$b4?7^\$7\$^\$9!#(Bruby-core\$B\$K\$bJ9\$\$\$F\$
\$k\$Y\$-\$+\$J!#(B

\$B;W\$\$\$D\$\$\$?\$s\$G\$9\$,!"(Bmap_pair \$B\$O\$I\$&\$G\$9\$+\$M!#(B

\$BB>\$N8@8l\$K\$D\$\$\$F\$O\$J\$+\$J\$+8+Ev\$?\$j\$^\$;\$s!#(B
enum.map {|e| [k,v] } \$B\$H(B Hash[assoc]
\$B\$NAH\$_9g\$o\$;\$G<B8=\$G\$-\$k\$N\$G!"(B
\$B\$J\$+\$J\$+\$=\$l\$i\$NAH\$_9g\$o\$;\$rDs6!\$9\$k\$H\$\$\$&OC\$K\$O\$J\$i\$J\$\$5\$\$,\$7\$^\$9!#(B

(\$BCf?H\$O%O%C%7%eI=\$8\$c\$J\$/\$FLZ\$G\$9\$,(B)
\$B\$+\$J\$jB?\$/\$N4X?t\$rDs6!\$7\$F\$\$\$k\$h\$&\$G\$9!#(B
map\$B\$G;O\$^\$kL>A0\$N4X?t\$b\$\$\$/\$D\$b\$”\$j\$^\$9!#(B

2010\$BG/(B11\$B7n(B29\$BF|(B13:56 Yukihiro M. [email protected]:

\$B5sF0\$H\$7\$F\$OBgJQ9%\$\$G\$9!#\$?\$@\$7!“(B categorize \$B\$O\$H\$b\$+\$/!”(B
hashmap \$B\$O\$\$\$^\$\$\$AL>A0\$K<+?.\$,;}\$F\$J\$\$\$N\$G!"99\$J\$kD4::(B(\$B4{B8(B
\$B\$N8@8l\$KN`;w5!G=\$O\$J\$\$\$+(B)\$B\$,I,MW\$+\$b\$7\$l\$^\$;\$s!#\$^\$?!"B>\$NJ}(B
\$B\$N\$40U8+\$b4?7^\$7\$^\$9!#(Bruby-core\$B\$K\$bJ9\$\$\$F\$
\$k\$Y\$-\$+\$J!#(B

\$B\$H\$j\$"\$(\$:(B categorize \$B\$@\$1(B C \$B\$G<BAu\$7\$F\$_\$^\$7\$?!#(B

(:update \$B\$N;EMM\$OG[Ns\$r:o8:\$9\$k\$?\$aHyL/\$KJQ\$(\$F\$"\$j\$^\$9!#(B)

# Index: enum.c

— enum.c (revision 29971)
+++ enum.c (working copy)
@@ -15,7 +15,7 @@
#include “id.h”

-static ID id_next;
+static ID id_next, id_call, id_seed, id_op, id_update;
#define id_each idEach
#define id_eqq idEqq
#define id_cmp idCmp
@@ -2595,6 +2595,160 @@ enum_slice_before(int argc, VALUE *argv,
return enumerator;
}

+struct categorize_arg {

• VALUE seed;
• VALUE op;
• VALUE update;
• VALUE result;
+};

+static VALUE
+categorize_update(struct categorize_arg *argp, VALUE ary, VALUE acc,
VALUE val)
+{

• if (argp->op != Qundef) {
• ``````   if (SYMBOL_P(argp->op))
``````
• ``````       return rb_funcall(acc, SYM2ID(argp->op), 1, val);
``````
• ``````   else
``````
• ``````       return rb_funcall(argp->op, id_call, 2, acc, val);
``````
• }
• else if (argp->update != Qundef) {
• ``````   if (SYMBOL_P(argp->update))
``````
• ``````       return rb_funcall(acc, SYM2ID(argp->update), 1, ary);
``````
• ``````   else
``````
• ``````       return rb_funcall(argp->update, id_call, 2, acc, ary);
``````
• }
• else {
• ``````   if (NIL_P(acc))
``````
• ``````       return rb_ary_new3(1, val);
``````
• ``````   else {
``````
• ``````       Check_Type(acc, T_ARRAY);
``````
• ``````       rb_ary_push(acc, val);
``````
• ``````       return acc;
``````
• ``````   }
``````
• }
+}

+static VALUE
+categorize_i(VALUE i, VALUE _arg, int argc, VALUE *argv)
+{

• struct categorize_arg *argp;
• VALUE ary, h;
• VALUE lastk, val, acc;
• long j;
• ENUM_WANT_SVALUE();
• argp = (struct categorize_arg *)_arg;
• ary = rb_yield(i);
• ary = rb_convert_type(ary, T_ARRAY, “Array”, “to_ary”);
• if (RARRAY_LEN(ary) < 2) {
• ``````   rb_raise(rb_eArgError, "array too short");
``````
• }
• lastk = RARRAY_PTR(ary)[RARRAY_LEN(ary)-2];
• val = RARRAY_PTR(ary)[RARRAY_LEN(ary)-1];
• h = argp->result;
• for (j = 0; j < RARRAY_LEN(ary) - 2; j++) {
• ``````   VALUE k = RARRAY_PTR(ary)[j];
``````
• ``````   VALUE h2;
``````
• ``````   h2 = rb_hash_lookup2(h, k, Qundef);
``````
• ``````   if (h2 == Qundef) {
``````
• ``````       h2 = rb_hash_new();
``````
• ``````       rb_hash_aset(h, k, h2);
``````
• ``````   }
``````
• ``````   h = h2;
``````
• }
• acc = rb_hash_lookup2(h, lastk, Qundef);
• if (acc == Qundef) {
• ``````   if (argp->seed == Qundef)
``````
• ``````       acc = val;
``````
• ``````   else
``````
• ``````       acc = categorize_update(argp, ary, argp->seed, val);
``````
• }
• else {
• ``````   acc = categorize_update(argp, ary, acc, val);
``````
• }
• rb_hash_aset(h, lastk, acc);
• return Qnil;
+}

+/*

• call-seq:
• enum.categorize([opts]) {|elt| [key1, …, val] } → hash
• categorizes the elements in enum and returns a hash.
• The block is called for each elements in enum.
• The block should return an array which contains
• one or more keys and one value.
• The keys and value are used to construct the result hash.
• If two or more keys are provided
• (i.e. the length of the array is longer than 2),
• the result hash will be nested.
• The value of innermost hash is an array which contains values for
• corresponding keys.
• (This behavior can be customized by :seed, :op and :update option.)
• a = [{:fruit => “banana”, :color => “yellow”, :taste => “sweet”},
• ``````   {:fruit => "melon", :color => "green", :taste => "sweet"},
``````
• ``````   {:fruit => "grapefruit", :color => "yellow", :taste =>
``````

“tart”}]

• p a.categorize {|h| h.values_at(:color, :fruit) }
• #=> {“yellow”=>[“banana”, “grapefruit”], “green”=>[“melon”]}
• pp a.categorize {|h| h.values_at(:taste, :color, :fruit) }
• #=> {“sweet”=>{“yellow”=>[“banana”], “green”=>[“melon”]},
• # “tart”=>{“yellow”=>[“grapefruit”]}}

• This method can take an option hash.
• Available options are follows:
• :seed specifies seed value.
• :op specifies a procedure from seed and value to next seed.
• :update specifies a procedure from seed and block value to next
seed.
• The default behavior, array construction, can be implemented as
follows.
• :seed => nil
• :op => lambda {|s, v| !s ? [v] : (s << v) }
• */
+static VALUE
+enum_categorize(int argc, VALUE *argv, VALUE enumerable)
+{
• VALUE opts;
• struct categorize_arg arg;
• RETURN_ENUMERATOR(enumerable, 0, 0);
• rb_scan_args(argc, argv, “01”, &opts);
• if (NIL_P(opts)) {
• ``````   arg.seed = Qnil;
``````
• ``````   arg.op = Qundef;
``````
• ``````   arg.update = Qundef;
``````
• }
• else {
• ``````   opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
``````
• ``````   arg.seed = rb_hash_lookup2(opts, ID2SYM(id_seed), Qundef);
``````
• ``````   arg.op = rb_hash_lookup2(opts, ID2SYM(id_op), Qundef);
``````
• ``````   arg.update = rb_hash_lookup2(opts, ID2SYM(id_update), Qundef);
``````
• ``````   if (arg.op != Qundef && arg.update != Qundef) {
``````
• ``````       rb_raise(rb_eArgError, "both :update and :op specified");
``````
• ``````   }
``````
• ``````   if (arg.op != Qundef && !SYMBOL_P(arg.op))
``````
• ``````       arg.op = rb_convert_type(arg.op, T_DATA, "Proc",
``````

“to_proc”);

• ``````   if (arg.update != Qundef && !SYMBOL_P(arg.update))
``````
• ``````       arg.update = rb_convert_type(arg.update, T_DATA, "Proc",
``````

“to_proc”);

• }
• arg.result = rb_hash_new();
• rb_block_call(enumerable, id_each, 0, 0, categorize_i,
(VALUE)&arg);
• return arg.result;
+}

/*

• The `Enumerable` mixin provides collection classes with
• several traversal and searching methods, and with the ability to
@@ -2662,6 +2816,11 @@ Init_Enumerable(void)
-1);
-1);

id_next = rb_intern(“next”);

• id_call = rb_intern(“call”);

• id_seed = rb_intern(“seed”);

• id_op = rb_intern(“op”);

• id_update = rb_intern(“update”);
}
Index: test/ruby/test_enum.rb
===================================================================
— test/ruby/test_enum.rb (revision 29971)
+++ test/ruby/test_enum.rb (working copy)
@@ -384,4 +384,33 @@ class TestEnumerable < Test::Unit::TestC
ss.slice_before(/\A…\z/).to_a)
end

• def test_categorize

• assert_equal((1…6).group_by {|i| i % 3 },

• ``````            (1..6).categorize {|e| [e % 3, e] })
``````
• assert_equal(Hash[ [ [“a”, 100], [“b”, 200] ] ],

• ``````            [ ["a", 100], ["b", 200] ].categorize(:op=>lambda
``````

{|x,y| y }) {|e| e })

• h = { “n” => 100, “m” => 100, “y” => 300, “d” => 200, “a” => 0 }
• assert_equal(h.invert,
• ``````            h.categorize(:op=>lambda {|x,y| y }) {|k, v| [v, k] })
``````
• assert_equal({“f”=>1, “o”=>2, “b”=>2, “a”=>2, “r”=>1, “z”=>1},
• ``````            "foobarbaz".split(//).categorize(:op=>:+) {|ch| [ch,
``````

1] })

• assert_equal({“f”=>1, “o”=>2, “b”=>2, “a”=>2, “r”=>1, “z”=>1},
• ``````            "foobarbaz".split(//).categorize(:update=>lambda
``````

{|s, a| s + a.last }) {|ch| [ch, 1] })

• assert_equal({“f”=>[“f”, 1],
• ``````             "o"=>["o", 1, "o", 1],
``````
• ``````             "b"=>["b", 1, "b", 1],
``````
• ``````             "a"=>["a", 1, "a", 1],
``````
• ``````             "r"=>["r", 1],
``````
• ``````             "z"=>["z", 1]},
``````
• ``````            "foobarbaz".split(//).categorize(:seed=>[],
``````

:update=>:+) {|ch| [ch, 1] })

• assert_raise(ArgumentError) { [0].categorize {|e| [] } }
• assert_raise(ArgumentError) { [0].categorize {|e| [1] } }
• assert_equal(
• `````` {"f"=>{"o"=>{"o"=>{:c=>1}}},
``````
• ``````  "b"=>{"a"=>{"r"=>{:c=>1},
``````
• ``````              "z"=>{:c=>1}}}},
``````
• `````` %w[foo bar baz].categorize(:op=>:+) {|s| s.split(//) + [:c, 1] })
``````
• end

end

\$B1sF#\$G\$9!#(B

2010\$BG/(B11\$B7n(B28\$BF|(B0:26 Masaya TARUI [email protected]:

inject\$B\$NN`;w\$G!";}\$A\$^\$o\$9\$N\$,%V%m%C%/\$N7k2L\$G\$J\$/Bh0l0z?t\$H\$\$\$&%a%=%C%I\$rMQ0U\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!)(B

snip

p ary.apply_to({}){|r,e|
r[e[1]] = e[0]
}

\$BC/\$b\$D\$C\$3\$s\$G\$^\$;\$s\$,!“(Bwith_object
\$B\$b\$?\$^\$K\$O;W\$\$=P\$7\$F\$”\$2\$F\$/\$@\$5\$\$!#(B

p ary.each.with_object({}) {|e, r| r[e[1]] = e[0] }

\$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m\$G\$9(B

In message “Re: [ruby-dev:42660] Re: Enumerable#categorize”
on Tue, 30 Nov 2010 12:08:12 +0900, Tanaka A. [email protected]
writes:

|2010\$BG/(B11\$B7n(B29\$BF|(B13:56 Yukihiro M. [email protected]:
|
|> \$B5sF0\$H\$7\$F\$OBgJQ9%\$\$G\$9!#\$?\$@\$7!“(B categorize \$B\$O\$H\$b\$+\$/!”(B
|> hashmap \$B\$O\$\$\$^\$\$\$AL>A0\$K<+?.\$,;}\$F\$J\$\$\$N\$G!"99\$J\$kD4::(B(\$B4{B8(B
|> \$B\$N8@8l\$KN`;w5!G=\$O\$J\$\$\$+(B)\$B\$,I,MW\$+\$b\$7\$l\$^\$;\$s!#\$^\$?!"B>\$NJ}(B
|> \$B\$N\$40U8+\$b4?7^\$7\$^\$9!#(Bruby-core\$B\$K\$bJ9\$\$\$F\$
\$k\$Y\$-\$+\$J!#(B
|
|\$B;W\$\$\$D\$\$\$?\$s\$G\$9\$,!"(Bmap_pair \$B\$O\$I\$&\$G\$9\$+\$M!#(B

hashmap\$B\$b(Bmap_pair\$B\$b:#\$R\$H\$DL>A0\$KG<F@\$,\$\$\$+\$J\$\$\$N\$G!"\$3\$A\$i\$O(B
\$B%Z%s%G%#%s%0\$G!#(B

categorize\$B\$O\$=\$s\$J\$K0-\$/\$J\$\$\$h\$&\$J5\$\$,\$7\$F\$\$\$k\$N\$G\$9\$,!“\$J\$K(B
\$B\$+Dq93\$,\$”\$k\$N\$O(B group_by \$B\$H\$+\$J\$j;w\$F\$\$\$k\$3\$H\$N\$h\$&\$J5\$\$,\$7(B
\$B\$F\$-\$^\$7\$?!#(Bcategorize\$B\$O(Bgroup_by\$B\$N%9!<%Q!<%;%C%H\$J\$N\$G\$9\$,!“(B
\$B\$I\$&@3\$_J,\$1\$k\$+!“5\$\$K\$J\$j\$^\$9!#\$^\$?!”(Bseed, op, update\$B\$J\$I\$N(B
\$B%*%W%7%g%s\$,JXMx\$J\$N\$OJ,\$+\$j\$^\$9\$,!”\$3\$l\$r;H\$&\$H!VJ,N`!W\$8\$c(B
\$B\$J\$/\$J\$C\$F\$7\$^\$&\$N\$b5\$\$K\$J\$kE@\$G\$9!#(B

\$B7z@_E*\$G\$J\$/\$F?=\$7Lu\$J\$\$!#8=;~E@\$N0U8+\$H\$7\$F\$O(B

• \$B5!G=\$NF3F~\$K\$OG<F@\$7\$?!#(B
• \$B4pK\5!G=\$O(B group_by \$B\$H:.Mp\$r>7\$/4mW|\$,>/\$7\$@\$1\$"\$k!#(B
\$B\$,!"CWL?E*\$JLdBj\$@\$H\$O;W\$C\$F\$\$\$J\$\$(B
• seed, op, update\$B\$J\$I\$N%*%W%7%g%s\$r;H\$&\$H(B categorize \$B\$H\$\$(B
\$B\$&L>A0\$,<(\$95!G=\$G\$O\$J\$/\$J\$k5\$\$,\$9\$k!#(B
• \$B\$h\$jE,@Z\$@\$H;W\$(\$kL>A0\$,\$"\$l\$P!"A4LL;?@.\$K2s\$k(B

\$B\$3\$s\$J46\$8\$G\$7\$g\$&\$+!#<+J,\$G\$b40A4\$K<+3P\$7\$F\$\$\$k\$o\$1\$G\$O\$J\$\$(B
\$B\$N\$G\$9\$,!"7k6I!“J,N`\$7\$F\$k\$N\$G\$O\$J\$/!”!V(Bmap(or hash)\$B\$K(B
transform\$B\$K\$7\$F\$\$\$k!W\$N\$@\$H\$\$\$&\$3\$H\$,6/D4\$5\$l\$?L>A0\$@\$HF10U(B
\$B\$7\$d\$9\$\$\$+\$b\$7\$l\$^\$;\$s!#(B

``````                            \$B\$^\$D\$b\$H(B \$B\$f\$-\$R\$m(B /:|)
``````