From 123045a57056b997165be4963cbf62120a967fec Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Mon, 25 Jan 2021 16:59:46 +0100 Subject: [PATCH] lib/attrsets: add cartesianProductOfSets function --- doc/functions/library/attrsets.xml | 39 +++++++++++++++++ lib/attrsets.nix | 19 ++++++++- lib/default.nix | 2 +- lib/tests/misc.nix | 67 ++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) diff --git a/doc/functions/library/attrsets.xml b/doc/functions/library/attrsets.xml index 3c5823c2589..7ef0d16624c 100644 --- a/doc/functions/library/attrsets.xml +++ b/doc/functions/library/attrsets.xml @@ -1711,4 +1711,43 @@ recursiveUpdate +
+ <function>lib.attrsets.cartesianProductOfSets</function> + + cartesianProductOfSets :: AttrSet -> [ AttrSet ] + + + + + + Return the cartesian product of attribute set value combinations. + + + + + + set + + + + An attribute set with attributes that carry lists of values. + + + + + + + Creating the cartesian product of a list of attribute values + [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] +]]> + +
+ diff --git a/lib/attrsets.nix b/lib/attrsets.nix index d91d7a0cd47..0ce3aaeca45 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -183,6 +183,24 @@ rec { else []; + /* Return the cartesian product of attribute set value combinations. + + Example: + cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } + => [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] + */ + cartesianProductOfSets = attrsOfLists: + lib.foldl' (listOfAttrs: attrName: + concatMap (attrs: + map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} + ) listOfAttrs + ) [{}] (attrNames attrsOfLists); + /* Utility function that creates a {name, value} pair as expected by builtins.listToAttrs. @@ -493,5 +511,4 @@ rec { zipWithNames = zipAttrsWithNames; zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; - } diff --git a/lib/default.nix b/lib/default.nix index 803f1f76564..50320669e28 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -78,7 +78,7 @@ let zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil recursiveUpdate matchAttrs overrideExisting getOutput getBin getLib getDev getMan chooseDevOutputs zipWithNames zip - recurseIntoAttrs dontRecurseIntoAttrs; + recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets; inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1 concatMap flatten remove findSingle findFirst any all count optional optionals toList range partition zipListsWith zipLists diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 35a5801c724..0d249968402 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -660,4 +660,71 @@ runTests { expected = [ [ "foo" ] [ "foo" "" "bar" ] [ "foo" "bar" ] ]; }; + testCartesianProductOfEmptySet = { + expr = cartesianProductOfSets {}; + expected = [ {} ]; + }; + + testCartesianProductOfOneSet = { + expr = cartesianProductOfSets { a = [ 1 2 3 ]; }; + expected = [ { a = 1; } { a = 2; } { a = 3; } ]; + }; + + testCartesianProductOfTwoSets = { + expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; }; + expected = [ + { a = 1; b = 10; } + { a = 1; b = 20; } + ]; + }; + + testCartesianProductOfTwoSetsWithOneEmpty = { + expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; }; + expected = [ ]; + }; + + testCartesianProductOfThreeSets = { + expr = cartesianProductOfSets { + a = [ 1 2 3 ]; + b = [ 10 20 30 ]; + c = [ 100 200 300 ]; + }; + expected = [ + { a = 1; b = 10; c = 100; } + { a = 1; b = 10; c = 200; } + { a = 1; b = 10; c = 300; } + + { a = 1; b = 20; c = 100; } + { a = 1; b = 20; c = 200; } + { a = 1; b = 20; c = 300; } + + { a = 1; b = 30; c = 100; } + { a = 1; b = 30; c = 200; } + { a = 1; b = 30; c = 300; } + + { a = 2; b = 10; c = 100; } + { a = 2; b = 10; c = 200; } + { a = 2; b = 10; c = 300; } + + { a = 2; b = 20; c = 100; } + { a = 2; b = 20; c = 200; } + { a = 2; b = 20; c = 300; } + + { a = 2; b = 30; c = 100; } + { a = 2; b = 30; c = 200; } + { a = 2; b = 30; c = 300; } + + { a = 3; b = 10; c = 100; } + { a = 3; b = 10; c = 200; } + { a = 3; b = 10; c = 300; } + + { a = 3; b = 20; c = 100; } + { a = 3; b = 20; c = 200; } + { a = 3; b = 20; c = 300; } + + { a = 3; b = 30; c = 100; } + { a = 3; b = 30; c = 200; } + { a = 3; b = 30; c = 300; } + ]; + }; }