Concise way of "updating" element in nested collections
I think you need an id field to be sure that you're updating the correct combatant. I assume it's possible to have multiple combatants with the same hp and attack.
And in that case it makes sense to store a group as a Map of id to combatant. This makes it really simple to update a value by id and there's no need to check for existence first. See the tryUpdateCombatantById function below.
You can add small helper functions to update specific record fields with a function that is passed in.


Here's the code with all of those changes:

type Combatant = {
    id : int
    hp : int
    attack : int
}

type Battle = { combatantGroups : Map&lt;int, Combatant&gt; list }

let updateHp f combatant = { combatant with hp = f combatant.hp }
let removeHp attack = updateHp (fun hp -&gt; hp - attack)

let tryUpdateCombatantById combatantId f group =
    group
    |&gt; Map.tryFind combatantId
    |&gt; Option.map (fun c -&gt; group |&gt; Map.add c.id (f c))
    |&gt; Option.defaultValue group

let applyAttack targetId attacker battle =
    { battle with
        combatantGroups =
            battle.combatantGroups
            |&gt; List.map (tryUpdateCombatantById targetId (removeHp attacker.attack)) }