Enumerable#categorize

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

$B$^[email protected](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 [email protected]@.$9$k%a%=%C%I$G$9!#(B

$B$?$H$($P!“0J2<$N$h$&$JG[Ns$,$”$C$?$H$7$^$7$g$&!#(B
($B$h$j8=<BE*$J>u67$rA[Dj$9$k$J$i!"(BCSV $B%U%!%$%k$r(B CSV.read
[email protected]$3$&$$$&G[Ns$,[email protected]$i$l$?$H9M$($F$/[email protected]$5$$(B)

ary = [
[“matz”, “Yukihiro M.”],
[“nobu”, “Nobuyoshi N.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],
[“naruse”, “NARUSE, Yui”],
[“ko1”, “SASADA Koichi”]
]

$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
([email protected]+F1L>$r9MN8$7$F!"%O%C%7%e$NCM$OBh0lMWAG$NG[Ns$H$7$^$7$g$&(B)

[email protected]$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”],
“Nobuyoshi N.”=>[“nobu”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],
“SASADA Koichi”=>[“ko1”]}

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”],
“Nobuyoshi N.”=>[“nobu”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],
“SASADA Koichi”=>[“ko1”]}

$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/[email protected]$^$9!#(B
$B$D$^$j!"(B[“matz”, “Yukihiro M.”][1] $B$H$7$F(B “Yukihiro
Matsumoto” $B$H$$$&%-!<[email protected]$F!"(B
[“matz”, “Yukihiro M.”][0] $B$H$7$F(B “matz”
$B$H$$$&[email protected]$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 ([email protected]$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%[email protected]@.(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%[email protected]@.$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
    [email protected]@.$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.”],
    “n”=>[“NARUSE, Yui”, “Nobuyoshi N.”],
    “a”=>[“Tanaka A.”],
    “u”=>[“Usaku NAKAMURA”],
    “k”=>[“SASADA Koichi”]}

    $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$+!"$^[email protected]$1M_$7$$$H$+!"(B
    $B$=$&$$$&MQES$K$b;H$($^$9!#(B
    ($B$3$l$i$K$D$$$F$O%a%b%j8zN([email protected]$+$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”,
    “Nobuyoshi N.”=>“nobu”,
    “Tanaka A.”=>“akr”,
    “Usaku NAKAMURA”=>“usa”,
    “NARUSE, Yui”=>“naruse”,
    “SASADA Koichi”=>“ko1”}

    $B$J$*!"%V%m%C%/0z?t$N(B ks [email protected]@.$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[[email protected]@.$7$?$/$J$$!”$H$$$&>u67$b$"$j$^$9!#(B
    $BNc$($P!"$$$/$D$"$k$N$+?t$($?$$$H$$$&[email protected]$1$J$i!"(B
    $BG[[email protected]@.$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[[email protected]@.$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 [email protected]$$$?$$(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>[email protected]>90>-(B", “KONISHI Hiromasa”], nil],
    “aamine”=>[["[email protected]:(B", “Minero A.”], ["[email protected]$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%[email protected]$+$i$G$9(B)

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

$BG[Ns$+$i(B hash [email protected]@.$9$k%a%=%C%I$O8=>u(B enum.group_by $B$d(B
Hash[assoc] $B$,$"$j$^$9$,!"(B
categorize
[email protected]$$=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

:call-seq:

enum.categorize(ksel1, ksel2, …, vsel, [opts])

enum.categorize(ksel1, ksel2, …, vsel, [opts]) {|ks, vs| … }

categorizes the elements in enum and returns a hash.

This method assumes multiple elements for a category.

+categorize+ takes one or more key selectors,

one value selector and

an optional option hash.

It also takes an optional block.

The selectors specify how to extract a value from an element in

enum.

The key selectors, kselN, are used to extract hash keys from an

element.

If two or more key selectors are specified, the result hash will be

nested.

The value selector, vsel, is used for the values of innermost

hashes.

By default, all values extracted by vsel from the elements which

key selectors extracts same value are composed as an array.

The array is set to the values of the innermost hashes.

This behavior can be customized by the options: :seed, :op and

:update.

a = [{:fruit => “banana”, :color => “yellow”, :taste => “sweet”,

:price => 100},

{:fruit => “melon”, :color => “green”, :taste => “sweet”,

:price => 300},

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

“tart”, :price => 200}]

p a.categorize(:color, :fruit)

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

p a.categorize(:taste, :fruit)

#=> {“sweet”=>[“banana”, “melon”], “tart”=>[“grapefruit”]}

p a.categorize(:taste, :color, :fruit)

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

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

p a.categorize(:taste, :color)

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

In the above example, :fruit, :color and :taste is specified as

selectors.

There are several types of selectors as follows:

- 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]+.

So the selector :fruit extracts the value from the element

{:fruit => “banana”, :color => “yellow”, :taste => “sweet”, :price

=> 100}

as {…}[:fruit].

p a.categorize(lambda {|elt| elt[:fruit][4] }, :fruit)

#=> {“n”=>[“banana”, “melon”], “e”=>[“grapefruit”]}

When the key selectors returns same key for two or or more elements,

corresponding values extracted by the value selector are combined.

By default, all values are collected as an array.

:seed, :op and :update option in the option hash customizes this

behavior.

:seed option and :op option is similar to Enumerable#inject.

:seed option specifies an initial value.

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

:update option is same as :op option except it takes three

arguments instead of two:

keys, seed and element.

+to_proc+ method is used to convert :op and :update option to a

procedure.

So a symbol can be used for them.

# count categorized elements.

p a.categorize(:color, lambda {|e| 1 }, :op=>:+)

#=> {“yellow”=>2, “green”=>1}

p a.categorize(:color, :fruit, :seed=>"", :op=>:+)

#=> {“yellow”=>“bananagrapefruit”, “green”=>“melon”}

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

implemented as follows.

:seed => nil

:update => {|ks, s, v| !s ? [v] : (s << v) }

:op and :update option are disjoint.

ArgumentError is raised if both are specified.

The block for +categorize+ method converts combined values to

final innermost hash values.

p a.categorize(:color, :fruit) {|ks, vs| vs.join(",") }

#=> {“yellow”=>“banana,grapefruit”, “green”=>“melon”}

# calculates the average price for fruits of each color.

p a.categorize(:color, :price) {|ks, vs| vs.inject(0.0, &:+) /

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
([email protected]+F1L>$r9MN8$7$F!"%O%C%7%e$NCM$OBh0lMWAG$NG[Ns$H$7$^$7$g$&(B)

[email protected]$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/[email protected]$^$9!#(B
$B$D$^$j!"(B[“matz”, “Yukihiro M.”][1] $B$H$7$F(B “Yukihiro M.”
$B$H$$$&%-!<[email protected]$F!"(B
[“matz”, “Yukihiro M.”][0] $B$H$7$F(B “matz” $B$H$$$&[email protected]$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$([email protected]$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/[email protected]$^$9!#(B
$B$D$^$j!"(B[“matz”, “Yukihiro M.”][1] $B$H$7$F(B “Yukihiro M.”
$B$H$$$&%-!<[email protected]$F!"(B

[“matz”, “Yukihiro M.”][0] $B$H$7$F(B “matz”
$B$H$$$&[email protected]$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$([email protected]$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!"[email protected]$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
([email protected]+F1L>$r9MN8$7$F!"%O%C%7%e$NCM$OBh0lMWAG$NG[Ns$H$7$^$7$g$&(B)

[email protected]$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/[email protected]$^$9!#(B
$B$D$^$j!"(B[“matz”, “Yukihiro M.”][1] $B$H$7$F(B “Yukihiro M.”
$B$H$$$&%-!<[email protected]$F!"(B

[“matz”, “Yukihiro M.”][0] $B$H$7$F(B “matz”
$B$H$$$&[email protected]$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$([email protected]$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

:call-seq:

enum.unique_categorize(ksel1, ksel2, …, vsel, [opts]) -> hash

enum.unique_categorize(ksel1, ksel2, …, vsel, [opts]) {|s, v|

… } -> hash

categorizes the elements in enum and returns a hash.

This method assumes one element for a category by default.

+unique_categorize+ takes one or more key selectors,

one value selector and

an optional option hash.

It also takes an optional block.

The selectors specify how to extract a value from an element in

enum.

See Enumerable#categorize for details of selectors.

The key selectors, kselN, are used to extract hash keys from an

element.

If two or more key selectors are specified, the result hash will be

nested.

The value selector, vsel, is used for the values of innermost

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.

This behavior can be customized by :seed option and the block.

a = [{:fruit => “banana”, :color => “yellow”, :taste => “sweet”,

:price => 100},

{:fruit => “melon”, :color => “green”, :taste => “sweet”,

:price => 300},

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

“tart”, :price => 200}]

p a.unique_categorize(:fruit, :price)

#=> {“banana”=>100, “melon”=>300, “grapefruit”=>200}

p a.unique_categorize(:color, :price)

# ArgumentError

If the block is given, it is used for combining values in a

category.

The arguments for the block is a seed and the value extracted by

vsel.

The return value of the block is used as the next seed.

:seed option specifies the initial seed.

If :seed is not given, the first value for each category is used

for the seed.

p a.unique_categorize(:taste, :price) {|s, v| s + v }

#=> {“sweet”=>400, “tart”=>200}

p a.unique_categorize(:color, :price) {|s, v| s + v }

#=> {“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

:call-seq:

enum.category_count(ksel1, ksel2, …)

counts elements in enum for each category defined by the key

selectors.

a = [{:fruit => “banana”, :color => “yellow”, :taste => “sweet”,

:price => 100},

{:fruit => “melon”, :color => “green”, :taste => “sweet”,

:price => 300},

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

“tart”, :price => 200}]

p a.category_count(:color)

#=> {“yellow”=>2, “green”=>1}

p a.category_count(:taste)

#=> {“sweet”=>2, “tart”=>1}

p a.category_count(:taste, :color)

#=> {“sweet”=>{“yellow”=>1, “green”=>1}, “tart”=>{“yellow”=>1}}

The selectors specify how to extract a value from an element in

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([email protected]@.$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,[email protected]$OM}2r$7$^$9!#$,!"(B
categorize [email protected]$K!VMWAG$r%V%m%C%/$KEO$7$F!"$=$N%V%m%C%/$N(B
[email protected]@.CM$K$h$C$F%O%C%7%e$r:n$k%a%=%C%I!W$,M_$7$$$G$9!#(B

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

#=> {“Yukihiro M.”=>[“matz”],
“Nobuyoshi N.”=>[“nobu”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],
“SASADA Koichi”=>[“ko1”]}

$B$G$9!#$^[email protected](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]] }]
[email protected]$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 [email protected]@.$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([email protected]@.$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,[email protected]$OM}2r$7$^$9!#$,!"(B
categorize [email protected]$K!VMWAG$r%V%m%C%/$KEO$7$F!"$=$N%V%m%C%/$N(B
[email protected]@.CM$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.”],
| [“nobu”, “Nobuyoshi N.”],
| [“akr”, “Tanaka A.”],
| [“usa”, “Usaku NAKAMURA”],
| [“naruse”, “NARUSE, Yui”],
| [“ko1”, “SASADA Koichi”]
| ]

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

$B$G$9!#$^[email protected](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 [email protected]$H$+$^$G$O<gD%$7$^$;$s$,!"$3$C$A$,(B
[email protected]$KM_$7$$$G$9!#(B

|* $B%M%9%H$7$?%O%C%7%[email protected]@.(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]] }] [email protected]$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$$$&[email protected]$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]] }] [email protected]$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$$$&[email protected]$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 /:|)

[email protected]$G$9!#(B

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

h = ary.xxx{|e| [e[1],e[0]]}
#=> {“Yukihiro M.”=>[“matz”],
“Nobuyoshi N.”=>[“nobu”],
“Tanaka A.”=>[“akr”],
“Usaku NAKAMURA”=>[“usa”],
“NARUSE, Yui”=>[“naruse”],
“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

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.”],
[“nobu”, “Nobuyoshi N.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],
[“naruse”, “NARUSE, Yui”],
[“ko1”, “SASADA Koichi”]
]

$B$3$l$^$G$N(B map $B$O$=$N$^$^(B

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%[email protected]@.(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%[email protected]@.$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$"[email protected]$m$&!"$H$$$&[email protected]$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#[email protected]@.$5$l$?$H$-$K$I$&$9$k$+!"(B
$B$H$$$&[email protected]$,$"$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
[email protected]!"%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%[email protected]$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

2010$BG/(B11$B7n(B28$BF|(B0:02 SASADA Koichi [email protected]:

   "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.”],
[“nobu”, “Nobuyoshi N.”],
[“akr”, “Tanaka A.”],
[“usa”, “Usaku NAKAMURA”],

[“naruse”, “NARUSE, Yui”],
[“ko1”, “SASADA Koichi”]
]

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

$B$G(B {“Yukihiro M.”=>“matz”, “Nobuyoshi N.”=>“nobu”,
“Tanaka
Akira”=>“akr”, “Usaku NAKAMURA”=>“usa”, “NARUSE, Yui” =>“naruse”,
“SASADA Koichi”=>“ko1”}
$B$,[email protected]$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$^[email protected]@$,=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!#[email protected]$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
[email protected]$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$$$&[email protected]$,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

[email protected]$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%[email protected]@.$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

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 provieded

(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 keys, seed and value to next

seed.

The default behavior, array construction, can be implemented as

follows.

:seed => nil

: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

call-seq:

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!#[email protected]$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
[email protected]$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>[email protected]$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

$B$A$J$_$K!"(BHaskell $B$N(B Data.Map $B$O(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

http://www.haskell.org/ghc/docs/6.12.2/html/libraries/containers-0.3.0.0/Data-Map.html

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

$B5sF0$H$7$F$OBgJQ9%$$G$9!#[email protected]$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
[email protected]$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 [email protected]$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”

VALUE rb_mEnumerable;
-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)
    rb_define_method(rb_mEnumerable, “cycle”, enum_cycle, -1);
    rb_define_method(rb_mEnumerable, “chunk”, enum_chunk, -1);
    rb_define_method(rb_mEnumerable, “slice_before”, enum_slice_before,
    -1);
  • rb_define_method(rb_mEnumerable, “categorize”, enum_categorize,
    -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$/[email protected]$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!#[email protected]$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
|> [email protected]$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<[email protected]$,$$$+$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$^$&[email protected]$G$9!#(B

[email protected]_E*$G$J$/$F?=$7Lu$J$$!#8=;[email protected]$N0U8+$H$7$F$O(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
[email protected]$H$$$&$3$H$,6/D4$5$l$?L>[email protected]$HF10U(B
$B$7$d$9$$$+$b$7$l$^$;$s!#(B

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs