/* Return if an attribute from nested attribute set exists.
Example:
x = { a = { b = 3; }; }
hasAttrByPath ["a" "b"] x
=> true
hasAttrByPath ["z" "z"] x
=> false
Type:
hasAttrByPath :: [String] -> AttrSet -> Bool
*/
hasAttrByPath =
# A list of strings representing the attribute path to check from `set`
attrPath:
# The nested attribute set to check
e:
let attr = head attrPath;
in
if attrPath == [] then true
else if e ? ${attr}
then hasAttrByPath (tail attrPath) e.${attr}
else false;
/* Filter an attribute set recursively by removing all attributes for
which the given predicate return false.
Example:
filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
=> { foo = {}; }
Type:
filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet
*/
filterAttrsRecursive =
# Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
pred:
# The attribute set to filter
set:
listToAttrs (
concatMap (name:
let v = set.${name}; in
if pred name v then [
(nameValuePair name (
if isAttrs v then filterAttrsRecursive pred v
else v
))
] else []
) (attrNames set)
);
from which you can probably derive a deleteRecursive.
There’s a function called updateManyAttrsByPath that suits your needs. It’s a little verbose, but you can easily build very nice modification functions with it.
Let’s take your example of a deep deletion function on a nested set:
nix-repl> v = { a = { r = { s = 3; t = 4; }; b = { c = 1; d = 2; }; }; }
nix-repl> removeByPath = pathList: set:
lib.updateManyAttrsByPath [
{
path = lib.init pathList;
update = old:
lib.filterAttrs (n: v: n != (lib.last pathList)) old;
}
] set
nix-repl> :p v
{ a = { b = { c = 1; d = 2; }; r = { s = 3; t = 4; }; }; }
nix-repl> :p removeByPath [ "a" "b" "d" ] v
{ a = { b = { c = 1; }; r = { s = 3; t = 4; }; }; }
It would indeed be nice to have something in pure Nix that came as close to the features and expressiveness of jq, though.
What really helps me is Noogle. You can search for all builtin and library functions and filter by types as well. That’s how I found updateManyAttrsByPath as well