From 15a8d05ba592355f37442743bb3ce9fe55b72911 Mon Sep 17 00:00:00 2001 From: hsjobeki Date: Tue, 28 Feb 2023 11:04:19 +0100 Subject: init: lib.foldlAttrs - provide comprehensive example - add unit test --- lib/attrsets.nix | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/default.nix | 2 +- lib/tests/misc.nix | 31 ++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/attrsets.nix b/lib/attrsets.nix index 30952651adf40..d9a6eab0603ed 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -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: diff --git a/lib/default.nix b/lib/default.nix index 85303e0a5abfb..0424db36b2e99 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -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 diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 07d04f5356c7b..baa382f3e589c 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -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"]) { -- cgit 1.4.1