Merge pull request #254452 from flyingcircusio/lib-attrsToList

lib.attrsets.attrsToList: add function

authored by Silvan Mosberger and committed by GitHub 5323fbf7 26858d74

+56 -2
+30
lib/attrsets.nix
··· 542 542 attrs: 543 543 map (name: f name attrs.${name}) (attrNames attrs); 544 544 545 + /* 546 + Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs). 547 + Each element of the resulting list is an attribute set with these attributes: 548 + - `name` (string): The name of the attribute 549 + - `value` (any): The value of the attribute 550 + 551 + The following is always true: 552 + ```nix 553 + builtins.listToAttrs (attrsToList attrs) == attrs 554 + ``` 555 + 556 + :::{.warning} 557 + The opposite is not always true. In general expect that 558 + ```nix 559 + attrsToList (builtins.listToAttrs list) != list 560 + ``` 561 + 562 + This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. 563 + ::: 564 + 565 + Example: 566 + attrsToList { foo = 1; bar = "asdf"; } 567 + => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] 568 + 569 + Type: 570 + attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] 571 + 572 + */ 573 + attrsToList = mapAttrsToList nameValuePair; 574 + 545 575 546 576 /* Like `mapAttrs`, except that it recursively applies itself to 547 577 the *leaf* attributes of a potentially-nested attribute set:
+2 -2
lib/default.nix
··· 81 81 inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath 82 82 getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs 83 83 filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs 84 - mapAttrs' mapAttrsToList concatMapAttrs mapAttrsRecursive mapAttrsRecursiveCond 85 - genAttrs isDerivation toDerivation optionalAttrs 84 + mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive 85 + mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs 86 86 zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil 87 87 recursiveUpdate matchAttrs overrideExisting showAttrPath getOutput getBin 88 88 getLib getDev getMan chooseDevOutputs zipWithNames zip
+24
lib/tests/misc.nix
··· 20 20 expr = (builtins.tryEval (builtins.seq expr "didn't throw")); 21 21 expected = { success = false; value = false; }; 22 22 }; 23 + testingEval = expr: { 24 + expr = (builtins.tryEval expr).success; 25 + expected = true; 26 + }; 23 27 testingDeepThrow = expr: testingThrow (builtins.deepSeq expr expr); 24 28 25 29 testSanitizeDerivationName = { name, expected }: ··· 815 819 expr = overrideExisting { a = 3; b = 2; } { a = 1; }; 816 820 expected = { a = 1; b = 2; }; 817 821 }; 822 + 823 + testListAttrsReverse = let 824 + exampleAttrs = {foo=1; bar="asdf"; baz = [1 3 3 7]; fnord=null;}; 825 + exampleSingletonList = [{name="foo"; value=1;}]; 826 + in { 827 + expr = { 828 + isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs; 829 + isReverseToAttrsToList = attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList; 830 + testDuplicatePruningBehaviour = attrsToList (builtins.listToAttrs [{name="a"; value=2;} {name="a"; value=1;}]); 831 + }; 832 + expected = { 833 + isReverseToAttrsToList = true; 834 + isReverseToListToAttrs = true; 835 + testDuplicatePruningBehaviour = [{name="a"; value=2;}]; 836 + }; 837 + }; 838 + 839 + testAttrsToListsCanDealWithFunctions = testingEval ( 840 + attrsToList { someFunc= a: a + 1;} 841 + ); 818 842 819 843 # GENERATORS 820 844 # these tests assume attributes are converted to lists