Unverified Commit 4635079f authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

Merge pull request #218776 from hsjobeki/feature/lib-reduceAttrs

add: lib.foldlAttrs
parents feae6010 15a8d05b
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -333,6 +333,66 @@ rec {
      ) (attrNames set)
    );

   /*
    Like builtins.foldl' but for attribute sets.
    Iterates over every name-value pair in the given attribute set.
    The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`.

    Attention:
      There is a completely different function
      `lib.foldAttrs`
      which has nothing to do with this function, despite the similar name.

    Example:
      foldlAttrs
        (acc: name: value: {
          sum = acc.sum + value;
          names = acc.names ++ [name];
        })
        { sum = 0; names = []; }
        {
          foo = 1;
          bar = 10;
        }
      ->
        {
          sum = 11;
          names = ["bar" "foo"];
        }

      foldlAttrs
        (throw "function not needed")
        123
        {};
      ->
        123

      foldlAttrs
        (_: _: v: v)
        (throw "initial accumulator not needed")
        { z = 3; a = 2; };
      ->
        3

      The accumulator doesn't have to be an attrset.
      It can be as simple as a number or string.

      foldlAttrs
        (acc: _: v: acc * 10 + v)
        1
        { z = 1; a = 2; };
      ->
        121

    Type:
      foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a
  */
  foldlAttrs = f: init: set:
    foldl'
      (acc: name: f acc name set.${name})
      init
      (attrNames set);

  /* Apply fold functions to values grouped by key.

     Example:
+1 −1
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ let
      composeManyExtensions makeExtensible makeExtensibleWithCustomName;
    inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
      getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs
      filterAttrsRecursive foldAttrs collect nameValuePair mapAttrs
      filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
      mapAttrs' mapAttrsToList concatMapAttrs mapAttrsRecursive mapAttrsRecursiveCond
      genAttrs isDerivation toDerivation optionalAttrs
      zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
+31 −0
Original line number Diff line number Diff line
@@ -533,6 +533,37 @@ runTests {
    };
  };

  # code from example
  testFoldlAttrs = {
    expr = {
      example = foldlAttrs
        (acc: name: value: {
          sum = acc.sum + value;
          names = acc.names ++ [ name ];
        })
        { sum = 0; names = [ ]; }
        {
          foo = 1;
          bar = 10;
        };
      # should just return the initial value
      emptySet = foldlAttrs (throw "function not needed") 123 { };
      # should just evaluate to the last value
      accNotNeeded = foldlAttrs (_acc: _name: v: v) (throw "accumulator not needed") { z = 3; a = 2; };
      # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
      trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; };
    };
    expected = {
      example = {
        sum = 11;
        names = [ "bar" "foo" ];
      };
      emptySet = 123;
      accNotNeeded = 3;
      trivialAcc = 121;
    };
  };

  # code from the example
  testRecursiveUpdateUntil = {
    expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {