What does IndexBy do and how exactly does it work?
Observing the code reported in kguler's answer I note that it could be written more efficiently, if such an operation is desired. Specifically (f[#]->#)&
cannot be compiled because the result is a Rule
. It would be better to map f
directly to the values, then use AssociationThread
to construct the association.
Proposal
indexBy[f_][expr_] := expr ~indexBy~ f
indexBy[asc_Association, f_] := Values[asc] ~indexBy~ f
p : indexBy[expr_, f_] /;
! AtomQ[expr] || Message[indexBy::normal, 1, HoldForm @ p] :=
AssociationThread[f /@ expr, expr]
p : indexBy[_, _, __] /;
Message[indexBy::argt, indexBy, Length @ Unevaluated @ p, 1, 2] := Null
Timings
expr = Range[1*^6];
f = #^2 &;
IndexBy[expr, f] // AbsoluteTiming // First
indexBy[expr, f] // AbsoluteTiming // First
1.353077 0.404023
expr = AssociationThread @@ RandomReal[9, {2, 1*^6}];
f = Round;
IndexBy[expr, f] // AbsoluteTiming // First
indexBy[expr, f] // AbsoluteTiming // First
1.122064 0.372021
?? *`*IndexBy*
Following the usual spelunking
steps
ClearAttributes[IndexBy, {Protected, ReadProtected}]
?? IndexBy
reveals the code that defines IndexBy
. Simplifying (and ignoring argument type-checks) it is something like:
indexBy[f_][expr_]:=Association[(f[#]->#)&/@If[AssociationQ[expr],Values[expr],expr]]
indexBy[foo][Range[5]]
(* <|foo[1]->1,foo[2]->2,foo[3]->3,foo[4]->4,foo[5]->5|> *)
indexBy[foo][<|a -> x, b -> y, c -> z|>]
(* <|foo[x]->x,foo[y]->y,foo[z]->z|> *)