Recursively updating an attribute set

Take the following set as an example:

{
  top1 =  {
    middle1 = {
      bottom1 = 1;
    };
  };
  top2 = {
    middle2 = {
      bottom2 = 2;
    };
  };
}

I want to apply n updates to this set in the following format:

{ 
  "top1.middle1.bottom1" = 99; 
  "top2.middle2.bottom2" = 88; 
  # `n` entries
}

The result should be:

{
  top1 =  {
    middle1 = {
      bottom1 = 99;
    };
  };
  top2 = {
    middle2 = {
      bottom2 = 88;
    };
  };
}

I’ve figured out how to apply one update to the set using a function I wrote:

with pkgs.lib;
updateValue = (config: path: value:
  let
    parts = splitString "." path;
  in
  overrideExisting config (setAttrByPath parts value)
);

With this, I can do something like:

> updateValue set "top1.middle1.bottom1" 99

And get the expected result. Where I’m stuck is figuring out how to apply the entire list of updates recursively and end up with the finalized result. I can use mapAttrsToList to map updateValue across the set, but then I end up with a list of partially modified sets that I have to somehow combine again.

I’m hoping there’s just some internal lib function I’m missing that does all of this. Otherwise, I could use some help :slight_smile:

I don’t know if this is the optimal solution, but it does what I want:

updateValue = (config: path: value:
  let
    parts = splitString "." path;
  in
  overrideExisting config (setAttrByPath parts value)
);

overrideAll = (config: overrides:
  let
    overrideList = pkgs.lib.mapAttrsToList (name: value: [ "${name}" value ]) overrides;
  in
  builtins.foldl' (x: y: updateValue x (builtins.elemAt y 0) (builtins.elemAt y 1)) config overrideList
);

Then:

t = {
  top1 =  {
    middle1 = {
      bottom1 = 1;
    };
  };
  top2 = {
    middle2 = {
      bottom2 = 2;
    };
  };
};

u = { "top1.middle1.bottom1" = 99; "top2.middle2.bottom2" = 88; };

And calling it:

> :p overrideAll t u
{ top1 = { middle1 = { bottom1 = 99; }; }; top2 = { middle2 = { bottom2 = 88; }; }; }

There is lib.recursiveUpdate in the nixpkgs library to recursively merge two attribute sets

1 Like