# Functional programming with Associations

Suppose I have an association where keys are dates, and values are returns of some portfolio, and I want to convert the returns to cumulative returns. Now, this can be done as follows:

Association[
Module[ {porfolio = KeySort[port]},
]
]


But this seems clunky. Is there some more elegant way of achieving the aim?

• Can you post a short example of port? Mar 29 '17 at 21:54
• @Stitch Sure, here is a not-so-short example: dropbox.com/s/og07v3l8e020c3w/egport.m?dl=0 Mar 29 '17 at 22:28
• Does anyone know why Accumulate[ <|"a" -> 1, "b" -> 2, "c" -> 3|> ] doesn't work? I thought this kind of thing was missing from version 10 simply because there wasn't time to add it, but it still doesn't work in version 11.1. Mar 30 '17 at 2:24
• @Mr.Wizard Would that no be because the internal ordering of the association has nothing to do a priori with what you type in, and thus it is not clear what order you are accumulating in? Just a thought... Mar 30 '17 at 4:14
• @Mr. Wizard, on a Dataset[] without named rows, I can see Accumulate[] working on it. Otherwise... Mar 30 '17 at 5:49

Best I can tell you really just want AssociationThread. It circumvents the Association and the MapThread.

p = AssociationThread[Range[100], Range[100]];


That does what you were doing before in a few fewer calls. Also note that Associations support lots of functional programming stuff. KeyMap and Map exist of course and KeyValueMap is there too.

If you're wondering why FoldList doesn't do this natively consider that Fold andFoldList are supposed to be direct counter-parts. FoldList could return an Association with the keys realigned, but that wouldn't make sense with Fold. So there's no way to make an intuitive, compatible definition across both of them. And so best to one for neither of them. With AssociationThread rebuilding your association is trivial anyway, so I think they didn't really see the need either.

• There is a problem with this (which I pointed out in one of my comments to another answer) is that the ordering of keys in an association is (intentionally) non-deterministic, so one pretty much has to do a KeySort before this has any semantic content... Still, I had no idea AssociationThread existed! Mar 30 '17 at 0:07
• @IgorRivin Yes and no. It's like a hash-map with ordering. So it can be indexed just like a list. If you want something with keys ordered according to their hash values you can check out the internal SystemPrivateHashTable. Never figured out exactly how it works, so if you do post a Q&A type question on it for me to upvote. Mar 30 '17 at 0:14
• I am guessing that the ordering depends on the hashing algorithm, which is not guaranteed to remain whatever it is today... Mar 30 '17 at 0:18
• @IgorRivin Of course not. But few top-level structures (none I can currently think of) will impose an ordering on their equivalent of the hash table keys. You'll probably have to go internal if you want that. Mar 30 '17 at 0:20
• The documentation states that Sort and KeySort can be applied to associations to produce well-ordered associations. Furthermore, associations are documented to support numeric part indices and value updating from ordered lists. From these assertions, we must conclude that association element order is being actively preserved independently of the key representation (e.g. like Java's LinkedHashMap). Still, it would be nice if the documentation explicitly stated that association maintain their order rather than just these implicit statements. Mar 30 '17 at 5:49

The shortest (and faster) way is to use Accumulate with pre-computed sorted Association:

eg = <|"2012-08-29" -> -0.2, "2012-08-30" -> 0.2,
"2012-08-31" -> -0.05, "2012-09-04" -> 0.05, "2012-09-05" -> -0.5,
"2012-09-01" -> -0.2, "2012-09-07" -> -0.3, "2012-09-10" -> 0.9|>;
egs = KeySort@eg;
AssociationThread[Keys@egs, egs // Values // Accumulate // Exp]


<|"2012-08-29" -> 0.818731, "2012-08-30" -> 1., "2012-08-31" -> 0.951229, "2012-09-01" -> 0.778801, "2012-09-04" -> 0.818731, "2012-09-05" -> 0.496585, "2012-09-07" -> 0.367879, "2012-09-10" -> 0.904837|>

• That is a good way, though (to play devil's advocate :)) it ONLY works with Plus as the thing being accumulated... Mar 30 '17 at 1:58
• @IgorRivin Well, you know, they say there is a function for everything in MMA :) Mar 30 '17 at 2:00

These are not functional solutions, but they are fairly concise owing to the "transparency" of associations to operators like Map and Exp:

Module[{t = 0}, Map[t += # &, eg]] // Exp


or Set:

Module[{e = eg}, e[[All]] = e // Values // Accumulate // Exp; e]


These solutions assume that the association eg is already in the desired order (like the file linked in the question -- use KeySort@eg if necessary).

You could write a version of FoldList that works on Associations:

foldList[f_, assoc_Association] :=
MapThread[Rule, {Keys@assoc, FoldList[f, Values@assoc]}] // Association


Then you can simply do

Exp /@ foldList[ Plus, KeySort@port ]

• True, but the principle is still the same, and the whole disassembling-reassembling process seems inelegant... Mar 29 '17 at 22:29
• @Igor Absent something built in, I think one has to choose between disassembling-reassembling or for-loop-style iteration. Mar 29 '17 at 22:33
• @Igor Aside from that issue, are you hoping for something more elegant? I find it hard to imagine something more elegant than a FoldList-like solution. Mar 29 '17 at 22:36
• I agree, but it is not obvious that something is not built in (Associations support some, but not all, list operations, so it seems a little underbaked, so maybe more baking had occurred while we were not looking?!) Mar 29 '17 at 22:36
• I guess part of the point is that there is no canonical order on the keys of an association, so things like Fold don't a a priori make sense, so maybe you are right - this maybe the best one can hope for... Mar 29 '17 at 22:38