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$ Commented Feb 13, 2015 at 20:27
  • $\begingroup$ @Mr.Wizard I gave it a shot. $\endgroup$ 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$ Commented Feb 2, 2015 at 21:42

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.