10
$\begingroup$

I have the following Association:

as = <|{5, 2, 1} -> 3, {6, 1, 4} -> 9, {5, 1, 9} -> 12, {5, 2, 4} -> 6, {6, 2, 8} -> 10|>;

I want to total the values on the right side of the rules according to the middle values of the keys to get

<|2 -> 19, 1 -> 21|>

The only way I know how to do this is to transform the Association to a List:

li = as /. (Association | Rule) -> List

{{{5, 2, 1}, 3}, {{6, 1, 4}, 9}, {{5, 1, 9}, 12}, {{5, 2, 4}, 6}, {{6, 2, 8}, 10}}

li = GatherBy[li /. {{_, a_, _}, b_} :> {a, b}, First]

{{{2, 3}, {2, 6}, {2, 10}}, {{1, 9}, {1, 12}}}

Association @ MapThread[Rule, {Map[#[[1, 1]] &, li], Total /@ Map[Last, li, {2}]}]

<|2 -> 19, 1 -> 21|>

Is there a way to get this result without destroying the Association ?

$\endgroup$
1

3 Answers 3

13
$\begingroup$

Solutions based on GroupBy can be made a bit simpler by exploiting the fact that as itself can be used as a function, and I like the aesthetics of the curried form of Extract, where Extract[{2}] is equivalent to Extract[#, {2}]&:

In[1]:= GroupBy[Keys[as], Extract[{2}] -> as, Total]
Out[1]= <|2 -> 19, 1 -> 21|>
$\endgroup$
2
  • $\begingroup$ Impressive! How does the ` Extract[{2}] -> as` work? $\endgroup$
    – eldo
    Oct 31, 2015 at 19:00
  • 3
    $\begingroup$ First Keys gets all the keys from as, then you can Extract the second element from each key. This gives you the association <|2 -> {{5, 2, 1}, {5, 2, 4}, {6, 2, 8}}, 1 -> {{6, 1, 4}, {5, 1, 9}}|>, where GroupBy has already collected all the keys that share a common second element. The →as part effectively tells GroupBy to go back to the original association and treat the collected lists as keys (again), replacing {5,2,1} with 3, {6,1,4} with 9 and so on. $\endgroup$
    – Graumagier
    Oct 31, 2015 at 21:15
3
$\begingroup$

How about:

GroupBy[KeyValueMap[#[[2]] -> #2 &, as], First -> Last, Total]

<|2 -> 19, 1 -> 21|>

Or shorter, per @Kuba:

Merge[Total]@KeyValueMap[#[[2]] -> #2 &, as]
$\endgroup$
6
  • $\begingroup$ Merge[Total] will be shorter. I'm not sure if KeyValueMap fits since it destroys association. $\endgroup$
    – Kuba
    Oct 31, 2015 at 18:32
  • $\begingroup$ @Kuba Does it technically destroy the association? Or just map over the keys? $\endgroup$
    – kale
    Oct 31, 2015 at 18:35
  • $\begingroup$ I have Version 10.0 which doesn't know KeyValueMap $\endgroup$
    – eldo
    Oct 31, 2015 at 18:35
  • $\begingroup$ @eldo Oops, introduced in 10.1... $\endgroup$
    – kale
    Oct 31, 2015 at 18:37
  • $\begingroup$ @Kuba, ah, you're right. KeyValueMap returns a list for some insane reason. $\endgroup$
    – kale
    Oct 31, 2015 at 18:40
2
$\begingroup$

Not so compact

GroupBy[AssociationMap[Reverse][as], #[[2]] &, Total @* Keys]
$\endgroup$
1
  • 1
    $\begingroup$ It would be nice to have some kind of third argument for KeyMap to specify type of Merging. $\endgroup$
    – Kuba
    Oct 31, 2015 at 21:06

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.