13
$\begingroup$

I just saw the function IndexBy in the autocomplete suggestions and I was curious to know exactly how it works. Unfortunately, there's no trace of it in the documentation. It returns Association objects, so it must be new (or recently refurbished) in version 10.0.2

What does this function do are what syntax does it require? Does it have further perks that are not immediately obvious?

Edit: As mentioned in the comments, as an undocumented function this was liable to be removed. And, indeed, it has been removed from version 10.1.0. The good thing is, if anyone needs a function like this, there's now kguler and Mr. Wizard's implementations to choose from.

$\endgroup$
  • 2
    $\begingroup$ if it is not documented, then it is not meant to be used at user level. It might be a place holder for future version. $\endgroup$ – Nasser Feb 20 '15 at 12:40
  • $\begingroup$ Related answer $\endgroup$ – m_goldberg Feb 20 '15 at 13:16
  • $\begingroup$ @Nasser If it is indeed not meant to be used at user level, I'm curious about why it shows up in the autocomplete suggestions. $\endgroup$ – Emilio Pisanty Feb 20 '15 at 15:35
  • $\begingroup$ @episanty, I think all system symbols will show up in the autocomplete suggestions, though many are undocumented. For a complete list of such symbols: Select[Names["*"],MatchQ[ToExpression[#<>"::usage"],_MessageName]&] (it takes a minute to run). $\endgroup$ – Simon Woods Feb 20 '15 at 22:01
  • 3
    $\begingroup$ Note that IndexBy will be removed in a future version of Mathematica. It was something that was considered for 10.0.0 but didn't make the cut. $\endgroup$ – Stefan R Feb 26 '15 at 17:37
9
$\begingroup$
?? *`*IndexBy*

enter image description here

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|> *)
$\endgroup$
11
$\begingroup$

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
$\endgroup$
  • 1
    $\begingroup$ I just ran into this and had a question. On 10.1, the AtomQ version is placed above indexBy[expr_, f_] in the DownValues. So, that index[5, f] returns AssociationThread[5, 5]. This can be fixed by using indexBy[expr : Except[_?AtomQ, _], f_], but I'm not sure if that's the right thing to do. Any thoughts. $\endgroup$ – rcollyer Apr 20 '15 at 19:45
  • 1
    $\begingroup$ @rcollyer I read your message before and thought "okay, when I get v10.1 I'll fix this" but now that I am running 10.1 I realize this has nothing to do with version and everything to do with a poorly conceived definition. Feel free to be more blunt about my stupid mistakes next time! $\endgroup$ – Mr.Wizard May 12 '15 at 6:01
  • $\begingroup$ I didn't realize it was your mistake. I was just playing with it, broke it, and tried to fix it. Next time, do better. :D $\endgroup$ – rcollyer May 12 '15 at 13:48
  • $\begingroup$ @rcollyer By the way what do you think of the || form in the answer now? It is my attempt at using Simon's flow control from (7820). Do you make use of such things yourself now? (You expressed interest below that post.) $\endgroup$ – Mr.Wizard May 12 '15 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.