5
$\begingroup$

I have a list of associations. I will need to add 2 columns (or perform some other arithmetic operation) and put the answer in a new column. For example:

{<|Year->2014, ID->1, amt1->10, amt2->20|>
 <|Year->2015, ID->1, amt1->20, amt2->25|> 
 <|Year->2014, ID->2, amt1->11, amt2->21|>}

I want to get:

{<|Year->2014, ID->1, amt1->10, amt2->20, tot->30|>
 <|Year->2015, ID->1, amt1->20, amt2->25, tot->45|> 
 <|Year->2014, ID->2, amt1->11, amt2->21, tot->32|>}

Sometimes I will want to delete the amt1 and amt2, depending on what I'm doing at the time.

I've done this by converting this back into a list of values, operating on the list and putting it back into an association. But it seems like there must be a better way of doing this.

$\endgroup$
1

2 Answers 2

7
$\begingroup$

Given the data from the question:

data = {<|Year->2014, ID->1, amt1->10, amt2->20|>,
        <|Year->2015, ID->1, amt1->20, amt2->25|>,
        <|Year->2014, ID->2, amt1->11, amt2->21|>};

We can introduce the key tot which is the sum of amt1 and amt2 like this:

<| #, tot -> #[amt1] + #[amt2] |> & /@ data

(*
   {<| Year->2014, ID->1, amt1->10, amt2->20, tot->30 |>,
    <| Year->2015, ID->1, amt1->20, amt2->25, tot->45 |>,
    <| Year->2014, ID->2, amt1->11, amt2->21, tot->32 |>}
*)

Should we wish to drop the input keys amt1 and amt2:

<| KeyDrop[#, {amt1, amt2}], tot -> #[amt1] + #[amt2] |> & /@ data

(*
   {<| Year->2014, ID->1, tot->200 |>,
    <| Year->2015, ID->1, tot->500 |>,
    <| Year->2014, ID->2, tot->231 |>}
*)

In some situations, it might just be easiest to build the result associations from scratch:

<| Year -> #[Year], ID -> #[ID], tot -> #[amt1] + #[amt2] |> & /@ data

(*
   {<| Year->2014, ID->1, tot->30 |>,
    <| Year->2015, ID->1, tot->45 |>,
    <| Year->2014, ID->2, tot->32 |>}
*)
$\endgroup$
2
  • $\begingroup$ WReach, I imagine there is a nice solution to (19565) using associations but I cannot find a clean formulation. Would you please consider answering that? $\endgroup$
    – Mr.Wizard
    Commented Feb 13, 2015 at 20:27
  • $\begingroup$ @Mr.Wizard I gave it a shot. $\endgroup$
    – WReach
    Commented Feb 14, 2015 at 5:41
4
$\begingroup$
data = {<|Year -> 2014, ID -> 1, amt1 -> 10, amt2 -> 20|>,
        <|Year -> 2015, ID -> 1, amt1 -> 20, amt2 -> 25|> , 
        <|Year -> 2014, ID -> 2, amt1 -> 11, amt2 -> 21|>};

Don't trust me, it's my 3rd answer about associations, but it works, so here you go:

data[[All, Key[tot]]] = Total /@ data[[All, {Key[amt1], Key[amt2]}]];
data
{<|Year -> 2014, ID -> 1, amt1 -> 10, amt2 -> 20, tot -> 30|>,
 <|Year -> 2015, ID -> 1, amt1 -> 20, amt2 -> 25, tot -> 45|>, 
 <|Year -> 2014, ID -> 2, amt1 -> 11, amt2 -> 21, tot -> 32|>}

Instead of Part you can use Lookup:

Total /@ Lookup[data, {amt1, amt2}, 0]

which is shorter and can give default value 0.


more general:

SetAttributes[f, HoldFirst];
f[data_, newKey_, keys_, function_] := data[[All, newKey]] = (function @@@ Lookup[data, keys]);

f[data, "total", Key /@ {Year, ID}, List];
data

{<|Year -> 2014, ID -> 1, amt1 -> 10, amt2 -> 20, "total" -> {2014, 1}|>,
 <|Year -> 2015, ID -> 1, amt1 -> 20, amt2 -> 25, "total" -> {2015, 1}|>,
 <|Year -> 2014, ID -> 2, amt1 -> 11, amt2 -> 21, "total" -> {2014, 2}|>}
$\endgroup$
4
  • $\begingroup$ That works, thank you. Suppose I want to multiply the 2 elements, or divide one by the other? Can I somehow set up a formula to do that? Or to operate on more than 2 elements. For example, for 3 elements, to add the first 2 and subtract the third one. $\endgroup$ Commented Feb 2, 2015 at 21:13
  • $\begingroup$ I guess either. I just did an example by using the list of associations as just a list. i.e., data[[All,3]]*data[[All,4]]. This gives me back a list which I'd have to turn back into an association. Ideally my code would probably be clearer if I used the keys rather than indices. Also, if it seems that I should just work with lists, and not try to do this inside the association, I can do that. $\endgroup$ Commented Feb 2, 2015 at 21:19
  • $\begingroup$ Sorry, I see that my previous edit was not at all clear. What about a case where I want to divide amt1 by amt2? If I can do that, I should be able to handle most of the situations I'm encountering. $\endgroup$ Commented Feb 2, 2015 at 21:41
  • $\begingroup$ @MitchellKaplan Then you could do f[data, Key[div], Key /@ {amt1, amt2}, #/#2 &]; $\endgroup$
    – Kuba
    Commented Feb 2, 2015 at 21:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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