 # What is the cleanest way of updating an item in a list

Assume a list of “cash records”. The cash records have a `currency` and an `amount` field.
Now I want to write a function `add` that takes a cash record, and a list of cash records and does the following:

1. if there is an entry with the same currency, update the existing list by adding the amounts
2. if there is no entry with the same currency, append it to the list.

I’ve written the following code, but I’m unsure about the elegance of replace

``````    case find (\cash -> cash.currency == newCash.currency) cashList of
Some a ->
replace
[a]
[newCash]
cashList
None -> cashList <> [newCash]
``````

Any Ideas?

1 Like

Your solution works just fine, but worst case does two passes through the list. One to find the element and another to replace it.

In general, if you have a list where you want a key, in this case currency to be unique, I’d recommend using a map. `TextMap` would work really well for this.

If you want to stick with a list, I’d recommend one of three approaches for a functions like this: using the `break` function, using recursion, or using a fold:

``````data Cash = Cash with
currency : Text
amount : Decimal
deriving (Eq, Show)
-- Using `break`
addCash(newCash: Cash, cashList: [Cash]): [Cash] =
let
(h, t) = break (\cash -> cash.currency == newCash.currency) cashList
newt = case t of
c::t' -> (c with amount = c.amount + newCash.amount)::t'
[] -> [newCash]
in h ++ newt
-- Using recursion
addCash'(newCash: Cash, cashList: [Cash]): [Cash] =
case cashList of
[] -> [newCash]
c::t -> if c.currency == newCash.currency
then (c with amount = c.amount + newCash.amount)::t
else c :: addCash' (newCash, t)
-- using a fold
addCash''(newCash: Cash, cashList: [Cash]): [Cash] =
let
workfn c (processed, t) = if c.currency == newCash.currency
then (True, (c with amount = c.amount + newCash.amount)::t)
else (processed, c::t)
(processed, t) = foldr workfn (False, []) cashList
in if processed then t else newCash::t
let
cashList =
[ Cash with currency = "A"; amount = 100.0
, Cash with currency = "B"; amount = 100.0
, Cash with currency = "C"; amount = 100.0]
newCash = Cash with currency = "B"; amount = 100.0
newCash' = Cash with currency = "D"; amount = 100.0
cashList' = [ Cash with currency = "A"; amount = 100.0
, Cash with currency = "B"; amount = 200.0
, Cash with currency = "C"; amount = 100.0]
cashList'' =
[ newCash'
, Cash with currency = "A"; amount = 100.0
, Cash with currency = "B"; amount = 100.0
, Cash with currency = "C"; amount = 100.0]
cashList''' =
[ Cash with currency = "A"; amount = 100.0
, Cash with currency = "B"; amount = 100.0
, Cash with currency = "C"; amount = 100.0
, newCash']
``````
3 Likes

For completeness, here is a version using `TextMap`. As a bonus, you get commutativity on addition and equality on stashes of cash independent of currency-insertion order.

``````module Main where

import Daml.Script
import qualified DA.TextMap as TextMap
import DA.TextMap (TextMap)

data Cash = Cash (TextMap Decimal)
deriving (Eq, Show)

(===) : Eq a => a -> a -> Script ()
(===) a b = assert (a == b)

cash : Text -> Decimal -> Cash
cash cur amount =
Cash \$ TextMap.fromList [(cur, amount)]

addCash : Cash -> Cash -> Cash
addCash (Cash cash1) (Cash cash2) =
Cash \$ TextMap.merge
(\cur amount -> Some amount) -- don't change curs only in cash1
(\cur amount -> Some amount) -- don't change curs only in cash2
(\cur amount1 amount2 -> Some (amount1 + amount2)) -- add together amounts for curs in both
cash1
cash2

let
stash = Cash \$ TextMap.fromList [("A", 100.0), ("B", 100.0), ("C", 100.0)]
b = cash "B" 100.0
d = cash "D" 100.0
stashWithB = Cash \$ TextMap.fromList [("A", 100.0), ("B", 200.0), ("C", 100.0)]
stashWithD = Cash \$ TextMap.fromList [("A", 100.0), ("B", 100.0), ("C", 100.0), ("D", 100.0)]
stashWithBoth = Cash \$ TextMap.fromList [("A", 100.0), ("B", 200.0), ("C", 100.0), ("D", 100.0)]
addCash (cash "A" 100.0) (addCash (cash "B" 100.0) (cash "C" 100.0)) === stash