Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 60 additions & 4 deletions lib/attrsets.nix
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,11 @@ rec {
+ "update an attribute inside of it."
);

# We get the final result by applying all the updates on this level
# after having applied all the nested updates
# We use foldl instead of foldl' so that in case of multiple updates,
# intermediate values aren't evaluated if not needed
in
# We get the final result by applying all the updates on this level
# after having applied all the nested updates
# We use foldl instead of foldl' so that in case of multiple updates,
# intermediate values aren't evaluated if not needed
foldl (acc: el: el.update acc) withNestedMods split.right;

in
Expand Down Expand Up @@ -871,6 +871,62 @@ rec {
else
[ ];

/**
Recursively collect sets that verify a given predicate named `pred`
from the set `attrs`. The recursion is stopped when the predicate is
verified. This version of `collect` also collects the attribute paths
of the items.

# Inputs

`pred`

: Given an attribute's value, determine if recursion should stop.

`attrs`

: The attribute set to recursively collect.

# Type

```
collect' :: (AttrSet -> Bool) -> AttrSet -> [{ path :: [ String ], value :: x }]
```

# Examples
:::{.example}
## `lib.attrsets.collect'` usage example

```nix
collect' isList { a = { b = ["b"]; }; c = [1]; }
=> [
{ path = [ "a" "b" ]; value = [ "b" ];
{ path = [ "c" ]; value = [ 1 ]; }
]

collect (x: x ? outPath)
{ a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
=> [
{ path = [ "a" ]; value = { outPath = "a/"; }; }
{ path = [ "b" ]; value = { outPath = "b/"; }; }
]
```

:::
*/
collect' =
let
collect'' =
path: pred: value:
if pred value then
[ { inherit path value; } ]
else if isAttrs value then
concatMap ({ name, value }: collect'' (path ++ [ name ]) pred value) (attrsToList value)
else
[ ];
in
collect'' [ ];

/**
Return the cartesian product of attribute set value combinations.

Expand Down
1 change: 1 addition & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ let
foldlAttrs
foldAttrs
collect
collect'
nameValuePair
mapAttrs
mapAttrs'
Expand Down
79 changes: 79 additions & 0 deletions lib/tests/misc.nix
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ let
callPackageWith
cartesianProduct
cli
collect
collect'
composeExtensions
composeManyExtensions
concatLines
Expand Down Expand Up @@ -297,6 +299,83 @@ runTests {
];
};

testCollect = {
expr = [
(collect (x: x ? special) {
a.b.c.special = true;
x.y.z.special = false;
})
(collect (x: x == 1) {
a = 1;
b = 2;
c = 3;
d.inner = 1;
})
];
expected = [
[
{ special = true; }
{ special = false; }
]
[
1
1
]
];
};

testCollect' = {
expr = [
(collect' (x: x ? special) {
a.b.c.special = true;
x.y.z.special = false;
})
(collect' (x: x == 1) {
a = 1;
b = 2;
c = 3;
d.inner = 1;
})
];
expected = [
[
{
path = [
"a"
"b"
"c"
];
value = {
special = true;
};
}
{
path = [
"x"
"y"
"z"
];
value = {
special = false;
};
}
]
[
{
path = [ "a" ];
value = 1;
}
{
path = [
"d"
"inner"
];
value = 1;
}
]
];
};

testComposeExtensions = {
expr =
let
Expand Down