1 # General list operations.
5 inherit (lib.strings) toInt;
6 inherit (lib.trivial) compare min id;
7 inherit (lib.attrsets) mapAttrs;
11 inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
13 /* Create a list consisting of a single element. `singleton x` is
14 sometimes more convenient with respect to indentation than `[x]`
15 when x spans multiple lines.
17 Type: singleton :: a -> [a]
25 /* Apply the function to each element in the list. Same as `map`, but arguments
28 Type: forEach :: [a] -> (a -> b) -> [b]
36 forEach = xs: f: map f xs;
38 /* “right fold” a binary function `op` between successive elements of
39 `list` with `nul` as the starting value, i.e.,
40 `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
42 Type: foldr :: (a -> b -> b) -> b -> [a] -> b
45 concat = foldr (a: b: a + b) "z"
46 concat [ "a" "b" "c" ]
49 strange = foldr (int: str: toString (int + 1) + str) "a"
53 foldr = op: nul: list:
59 else op (elemAt list n) (fold' (n + 1));
62 /* `fold` is an alias of `foldr` for historic reasons */
63 # FIXME(Profpatsch): deprecate?
67 /* “left fold”, like `foldr`, but from the left:
68 `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`.
70 Type: foldl :: (b -> a -> b) -> b -> [a] -> b
73 lconcat = foldl (a: b: a + b) "z"
74 lconcat [ "a" "b" "c" ]
77 lstrange = foldl (str: int: str + toString (int + 1)) "a"
81 foldl = op: nul: list:
86 else op (foldl' (n - 1)) (elemAt list n);
87 in foldl' (length list - 1);
90 Reduce a list by applying a binary operator from left to right,
91 starting with an initial accumulator.
93 Before each application of the operator, the accumulator value is evaluated.
94 This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).
96 Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),
97 the initial accumulator argument is evaluated before the first iteration.
102 foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]
105 is (denotationally) equivalent to the following,
106 but with the added benefit that `foldl'` itself will never overflow the stack.
110 acc₁ = builtins.seq acc₀ (op acc₀ x₀ );
111 acc₂ = builtins.seq acc₁ (op acc₁ x₁ );
112 acc₃ = builtins.seq acc₂ (op acc₂ x₂ );
114 accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);
115 accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );
119 # Or ignoring builtins.seq
120 op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ
123 Type: foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc
126 foldl' (acc: x: acc + x) 0 [1 2 3]
130 /* The binary operation to run, where the two arguments are:
132 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration
133 2. `x`: The corresponding list element for this iteration
136 # The initial accumulator value
141 # The builtin `foldl'` is a bit lazier than one might expect.
142 # See https://github.com/NixOS/nix/pull/7158.
143 # In particular, the initial accumulator value is not forced before the first iteration starts.
145 (builtins.foldl' op acc list);
147 /* Map with index starting from 0
149 Type: imap0 :: (int -> a -> b) -> [a] -> [b]
152 imap0 (i: v: "${v}-${toString i}") ["a" "b"]
155 imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
157 /* Map with index starting from 1
159 Type: imap1 :: (int -> a -> b) -> [a] -> [b]
162 imap1 (i: v: "${v}-${toString i}") ["a" "b"]
165 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
167 /* Map and concatenate the result.
169 Type: concatMap :: (a -> [b]) -> [a] -> [b]
172 concatMap (x: [x] ++ ["z"]) ["a" "b"]
173 => [ "a" "z" "b" "z" ]
175 concatMap = builtins.concatMap or (f: list: concatLists (map f list));
177 /* Flatten the argument into a single list; that is, nested lists are
178 spliced into the top-level lists.
181 flatten [1 [2 [3] 4] 5]
188 then concatMap (y: flatten y) x
191 /* Remove elements equal to 'e' from a list. Useful for buildInputs.
193 Type: remove :: a -> [a] -> [a]
200 # Element to remove from the list
201 e: filter (x: x != e);
203 /* Find the sole element in the list matching the specified
204 predicate, returns `default` if no such element exists, or
205 `multiple` if there are multiple matching elements.
207 Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a
210 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
212 findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
214 findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
220 # Default value to return if element was not found.
222 # Default value to return if more than one element was found
226 let found = filter pred list; len = length found;
227 in if len == 0 then default
228 else if len != 1 then multiple
231 /* Find the first index in the list matching the specified
232 predicate or return `default` if no such element exists.
234 Type: findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b)
237 findFirstIndex (x: x > 3) null [ 0 6 4 ]
239 findFirstIndex (x: x > 9) null [ 0 6 4 ]
245 # Default value to return
250 # A naive recursive implementation would be much simpler, but
251 # would also overflow the evaluator stack. We use `foldl'` as a workaround
252 # because it reuses the same stack space, evaluating the function for one
253 # element after another. We can't return early, so this means that we
254 # sacrifice early cutoff, but that appears to be an acceptable cost. A
255 # clever scheme with "exponential search" is possible, but appears over-
256 # engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267
259 # - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred
260 # - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
262 # We start with index -1 and the 0'th element of the list, which satisfies the invariant
263 resultIndex = foldl' (index: el:
265 # No match yet before the current index, we need to check the element
267 # We have a match! Turn it into the actual index to prevent future iterations from modifying it
270 # Still no match, update the index to the next element (we're counting down, so minus one)
273 # There's already a match, propagate the index without evaluating anything
277 if resultIndex < 0 then
282 /* Find the first element in the list matching the specified
283 predicate or return `default` if no such element exists.
285 Type: findFirst :: (a -> bool) -> a -> [a] -> a
288 findFirst (x: x > 3) 7 [ 1 6 4 ]
290 findFirst (x: x > 9) 7 [ 1 6 4 ]
296 # Default value to return
301 index = findFirstIndex pred null list;
303 if index == null then
308 /* Return true if function `pred` returns true for at least one
311 Type: any :: (a -> bool) -> [a] -> bool
314 any isString [ 1 "a" { } ]
316 any isString [ 1 { } ]
319 any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
321 /* Return true if function `pred` returns true for all elements of
324 Type: all :: (a -> bool) -> [a] -> bool
327 all (x: x < 3) [ 1 2 ]
329 all (x: x < 3) [ 1 2 3 ]
332 all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
334 /* Count how many elements of `list` match the supplied predicate
337 Type: count :: (a -> bool) -> [a] -> int
340 count (x: x == 3) [ 3 2 3 4 6 ]
345 pred: foldl' (c: x: if pred x then c + 1 else c) 0;
347 /* Return a singleton list or an empty list, depending on a boolean
348 value. Useful when building lists with optional elements
349 (e.g. `++ optional (system == "i686-linux") firefox`).
351 Type: optional :: bool -> a -> [a]
359 optional = cond: elem: if cond then [elem] else [];
361 /* Return a list or an empty list, depending on a boolean value.
363 Type: optionals :: bool -> [a] -> [a]
366 optionals true [ 2 3 ]
368 optionals false [ 2 3 ]
374 # List to return if condition is true
375 elems: if cond then elems else [];
378 /* If argument is a list, return it; else, wrap it in a singleton
379 list. If you're using this, you should almost certainly
380 reconsider if there isn't a more "well-typed" approach.
388 toList = x: if isList x then x else [x];
390 /* Return a list of integers from `first` up to and including `last`.
392 Type: range :: int -> int -> [int]
401 # First integer in the range
403 # Last integer in the range
408 genList (n: first + n) (last - first + 1);
410 /* Return a list with `n` copies of an element.
412 Type: replicate :: int -> a -> [a]
420 replicate = n: elem: genList (_: elem) n;
422 /* Splits the elements of a list in two lists, `right` and
423 `wrong`, depending on the evaluation of a predicate.
425 Type: (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; }
428 partition (x: x > 2) [ 5 1 2 3 4 ]
429 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
431 partition = builtins.partition or (pred:
434 then { right = [h] ++ t.right; wrong = t.wrong; }
435 else { right = t.right; wrong = [h] ++ t.wrong; }
436 ) { right = []; wrong = []; });
438 /* Splits the elements of a list into many lists, using the return value of a predicate.
439 Predicate should return a string which becomes keys of attrset `groupBy` returns.
441 `groupBy'` allows to customise the combining function and initial value
444 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
445 => { true = [ 5 3 4 ]; false = [ 1 2 ]; }
446 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";}
447 {name = "xfce"; script = "xfce4-session &";}
448 {name = "icewm"; script = "icewmbg &";}
449 {name = "mate"; script = "gnome-session &";}
451 => { icewm = [ { name = "icewm"; script = "icewm &"; }
452 { name = "icewm"; script = "icewmbg &"; } ];
453 mate = [ { name = "mate"; script = "gnome-session &"; } ];
454 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ];
457 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
458 => { true = 12; false = 3; }
460 groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
462 groupBy = builtins.groupBy or (
467 r // { ${key} = (r.${key} or []) ++ [e]; }
470 /* Merges two lists of the same size together. If the sizes aren't the same
471 the merging stops at the shortest. How both lists are merged is defined
472 by the first argument.
474 Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c]
477 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
481 # Function to zip elements of both lists
488 (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
490 /* Merges two lists of the same size together. If the sizes aren't the same
491 the merging stops at the shortest.
493 Type: zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }]
496 zipLists [ 1 2 ] [ "a" "b" ]
497 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
499 zipLists = zipListsWith (fst: snd: { inherit fst snd; });
501 /* Reverse the order of the elements of a list.
503 Type: reverseList :: [a] -> [a]
507 reverseList [ "b" "o" "j" ]
511 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
513 /* Depth-First Search (DFS) for lists `list != []`.
515 `before a b == true` means that `b` depends on `a` (there's an
516 edge from `b` to `a`).
519 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
520 == { minimal = "/"; # minimal element
521 visited = [ "/home/user" ]; # seen elements (in reverse order)
522 rest = [ "/home" "other" ]; # everything else
525 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
526 == { cycle = "/"; # cycle encountered at this element
527 loops = [ "/" ]; # and continues to these elements
528 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
529 rest = [ "/home" "other" ]; # everything else
532 listDfs = stopOnCycles: before: list:
534 dfs' = us: visited: rest:
536 c = filter (x: before x us) visited;
537 b = partition (x: before x us) rest;
538 in if stopOnCycles && (length c > 0)
539 then { cycle = us; loops = c; inherit visited rest; }
540 else if length b.right == 0
541 then # nothing is before us
542 { minimal = us; inherit visited rest; }
543 else # grab the first one before us and continue
546 (tail b.right ++ b.wrong);
547 in dfs' (head list) [] (tail list);
549 /* Sort a list based on a partial ordering using DFS. This
550 implementation is O(N^2), if your ordering is linear, use `sort`
553 `before a b == true` means that `b` should be after `a`
558 toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
559 == { result = [ "/" "/home" "/home/user" "other" ]; }
561 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
562 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
563 loops = [ "/" ]; } # loops back to these elements
565 toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
566 == { result = [ "other" "/" "/home" "/home/user" ]; }
568 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
571 toposort = before: list:
573 dfsthis = listDfs true before list;
574 toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
579 else if dfsthis ? cycle
580 then # there's a cycle, starting from the current vertex, return it
581 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
582 inherit (dfsthis) loops; }
583 else if toporest ? cycle
584 then # there's a cycle somewhere else in the graph, return it
586 # Slow, but short. Can be made a bit faster with an explicit stack.
587 else # there are no cycles
588 { result = [ dfsthis.minimal ] ++ toporest.result; };
590 /* Sort a list based on a comparator function which compares two
591 elements and returns true if the first argument is strictly below
592 the second argument. The returned list is sorted in an increasing
593 order. The implementation does a quick-sort.
596 sort (a: b: a < b) [ 5 3 7 ]
599 sort = builtins.sort or (
604 pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
607 else if strictLess first el
608 then next { inherit left; right = [ el ] ++ right; }
610 next { left = [ el ] ++ left; inherit right; };
611 pivot = pivot' 1 { left = []; right = []; };
614 else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
616 /* Compare two lists element-by-element.
619 compareLists compare [] []
621 compareLists compare [] [ "a" ]
623 compareLists compare [ "a" ] []
625 compareLists compare [ "a" "b" ] [ "a" "c" ]
628 compareLists = cmp: a: b:
635 else let rel = cmp (head a) (head b); in
637 then compareLists cmp (tail a) (tail b)
640 /* Sort list using "Natural sorting".
641 Numeric portions of strings are sorted in numeric order.
644 naturalSort ["disk11" "disk8" "disk100" "disk9"]
645 => ["disk8" "disk9" "disk11" "disk100"]
646 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"]
647 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"]
648 naturalSort ["v0.2" "v0.15" "v0.0.9"]
649 => [ "v0.0.9" "v0.2" "v0.15" ]
653 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
654 prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
655 less = a: b: (compareLists compare (head a) (head b)) < 0;
657 map (x: elemAt x 1) (sort less prepared);
659 /* Return the first (at most) N elements of a list.
661 Type: take :: int -> [a] -> [a]
664 take 2 [ "a" "b" "c" "d" ]
670 # Number of elements to take
671 count: sublist 0 count;
673 /* Remove the first (at most) N elements of a list.
675 Type: drop :: int -> [a] -> [a]
678 drop 2 [ "a" "b" "c" "d" ]
684 # Number of elements to drop
687 list: sublist count (length list) list;
689 /* Whether the first list is a prefix of the second list.
691 Type: hasPrefix :: [a] -> [a] -> bool
694 hasPrefix [ 1 2 ] [ 1 2 3 4 ]
696 hasPrefix [ 0 1 ] [ 1 2 3 4 ]
702 take (length list1) list2 == list1;
704 /* Remove the first list as a prefix from the second list.
705 Error if the first list isn't a prefix of the second list.
707 Type: removePrefix :: [a] -> [a] -> [a]
710 removePrefix [ 1 2 ] [ 1 2 3 4 ]
712 removePrefix [ 0 1 ] [ 1 2 3 4 ]
718 if hasPrefix list1 list2 then
719 drop (length list1) list2
721 throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument";
723 /* Return a list consisting of at most `count` elements of `list`,
724 starting at index `start`.
726 Type: sublist :: int -> int -> [a] -> [a]
729 sublist 1 3 [ "a" "b" "c" "d" "e" ]
735 # Index at which to start the sublist
737 # Number of elements to take
741 let len = length list; in
743 (n: elemAt list (n + start))
744 (if start >= len then 0
745 else if start + count > len then len - start
748 /* The common prefix of two lists.
750 Type: commonPrefix :: [a] -> [a] -> [a]
753 commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]
755 commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]
757 commonPrefix [ 1 2 3 ] [ 4 5 6 ]
764 # Zip the lists together into a list of booleans whether each element matches
765 matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
766 # Find the first index where the elements don't match,
767 # which will then also be the length of the common prefix.
768 # If all elements match, we fall back to the length of the zipped list,
769 # which is the same as the length of the smaller list.
770 commonPrefixLength = findFirstIndex id (length matchings) matchings;
772 take commonPrefixLength list1;
774 /* Return the last element of a list.
776 This function throws an error if the list is empty.
778 Type: last :: [a] -> a
785 assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
786 elemAt list (length list - 1);
788 /* Return all elements but the last.
790 This function throws an error if the list is empty.
792 Type: init :: [a] -> [a]
799 assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
800 take (length list - 1) list;
803 /* Return the image of the cross product of some lists by a function.
806 crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
807 => [ "13" "14" "23" "24" ]
809 crossLists = builtins.trace
810 "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
811 (f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
814 /* Remove duplicate elements from the list. O(n^2) complexity.
816 Type: unique :: [a] -> [a]
822 unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
824 /* Intersects list 'e' and another list. O(nm) complexity.
827 intersectLists [ 1 2 3 ] [ 6 3 2 ]
830 intersectLists = e: filter (x: elem x e);
832 /* Subtracts list 'e' from another list. O(nm) complexity.
835 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
838 subtractLists = e: filter (x: !(elem x e));
840 /* Test if two lists have no common element.
841 It should be slightly more efficient than (intersectLists a b == [])
843 mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b);