1 /* General list operations. */
4 inherit (lib.strings) toInt;
5 inherit (lib.trivial) compare min id;
6 inherit (lib.attrsets) mapAttrs;
10 inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
12 /* Create a list consisting of a single element. `singleton x` is
13 sometimes more convenient with respect to indentation than `[x]`
14 when x spans multiple lines.
16 Type: singleton :: a -> [a]
24 /* Apply the function to each element in the list. Same as `map`, but arguments
27 Type: forEach :: [a] -> (a -> b) -> [b]
35 forEach = xs: f: map f xs;
37 /* “right fold” a binary function `op` between successive elements of
38 `list` with `nul` as the starting value, i.e.,
39 `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
41 Type: foldr :: (a -> b -> b) -> b -> [a] -> b
44 concat = foldr (a: b: a + b) "z"
45 concat [ "a" "b" "c" ]
48 strange = foldr (int: str: toString (int + 1) + str) "a"
52 foldr = op: nul: list:
58 else op (elemAt list n) (fold' (n + 1));
61 /* `fold` is an alias of `foldr` for historic reasons */
62 # FIXME(Profpatsch): deprecate?
66 /* “left fold”, like `foldr`, but from the left:
67 `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`.
69 Type: foldl :: (b -> a -> b) -> b -> [a] -> b
72 lconcat = foldl (a: b: a + b) "z"
73 lconcat [ "a" "b" "c" ]
76 lstrange = foldl (str: int: str + toString (int + 1)) "a"
80 foldl = op: nul: list:
85 else op (foldl' (n - 1)) (elemAt list n);
86 in foldl' (length list - 1);
89 Reduce a list by applying a binary operator from left to right,
90 starting with an initial accumulator.
92 Before each application of the operator, the accumulator value is evaluated.
93 This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl).
95 Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'),
96 the initial accumulator argument is evaluated before the first iteration.
101 foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ]
104 is (denotationally) equivalent to the following,
105 but with the added benefit that `foldl'` itself will never overflow the stack.
109 acc₁ = builtins.seq acc₀ (op acc₀ x₀ );
110 acc₂ = builtins.seq acc₁ (op acc₁ x₁ );
111 acc₃ = builtins.seq acc₂ (op acc₂ x₂ );
113 accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁);
114 accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ );
118 # Or ignoring builtins.seq
119 op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ
122 Type: foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc
125 foldl' (acc: x: acc + x) 0 [1 2 3]
129 /* The binary operation to run, where the two arguments are:
131 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration
132 2. `x`: The corresponding list element for this iteration
135 # The initial accumulator value
140 # The builtin `foldl'` is a bit lazier than one might expect.
141 # See https://github.com/NixOS/nix/pull/7158.
142 # In particular, the initial accumulator value is not forced before the first iteration starts.
144 (builtins.foldl' op acc list);
146 /* Map with index starting from 0
148 Type: imap0 :: (int -> a -> b) -> [a] -> [b]
151 imap0 (i: v: "${v}-${toString i}") ["a" "b"]
154 imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
156 /* Map with index starting from 1
158 Type: imap1 :: (int -> a -> b) -> [a] -> [b]
161 imap1 (i: v: "${v}-${toString i}") ["a" "b"]
164 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
166 /* Map and concatenate the result.
168 Type: concatMap :: (a -> [b]) -> [a] -> [b]
171 concatMap (x: [x] ++ ["z"]) ["a" "b"]
172 => [ "a" "z" "b" "z" ]
174 concatMap = builtins.concatMap or (f: list: concatLists (map f list));
176 /* Flatten the argument into a single list; that is, nested lists are
177 spliced into the top-level lists.
180 flatten [1 [2 [3] 4] 5]
187 then concatMap (y: flatten y) x
190 /* Remove elements equal to 'e' from a list. Useful for buildInputs.
192 Type: remove :: a -> [a] -> [a]
199 # Element to remove from the list
200 e: filter (x: x != e);
202 /* Find the sole element in the list matching the specified
203 predicate, returns `default` if no such element exists, or
204 `multiple` if there are multiple matching elements.
206 Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a
209 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
211 findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
213 findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
219 # Default value to return if element was not found.
221 # Default value to return if more than one element was found
225 let found = filter pred list; len = length found;
226 in if len == 0 then default
227 else if len != 1 then multiple
230 /* Find the first index in the list matching the specified
231 predicate or return `default` if no such element exists.
233 Type: findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b)
236 findFirstIndex (x: x > 3) null [ 0 6 4 ]
238 findFirstIndex (x: x > 9) null [ 0 6 4 ]
244 # Default value to return
249 # A naive recursive implementation would be much simpler, but
250 # would also overflow the evaluator stack. We use `foldl'` as a workaround
251 # because it reuses the same stack space, evaluating the function for one
252 # element after another. We can't return early, so this means that we
253 # sacrifice early cutoff, but that appears to be an acceptable cost. A
254 # clever scheme with "exponential search" is possible, but appears over-
255 # engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267
258 # - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred
259 # - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
261 # We start with index -1 and the 0'th element of the list, which satisfies the invariant
262 resultIndex = foldl' (index: el:
264 # No match yet before the current index, we need to check the element
266 # We have a match! Turn it into the actual index to prevent future iterations from modifying it
269 # Still no match, update the index to the next element (we're counting down, so minus one)
272 # There's already a match, propagate the index without evaluating anything
276 if resultIndex < 0 then
281 /* Find the first element in the list matching the specified
282 predicate or return `default` if no such element exists.
284 Type: findFirst :: (a -> bool) -> a -> [a] -> a
287 findFirst (x: x > 3) 7 [ 1 6 4 ]
289 findFirst (x: x > 9) 7 [ 1 6 4 ]
295 # Default value to return
300 index = findFirstIndex pred null list;
302 if index == null then
307 /* Return true if function `pred` returns true for at least one
310 Type: any :: (a -> bool) -> [a] -> bool
313 any isString [ 1 "a" { } ]
315 any isString [ 1 { } ]
318 any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
320 /* Return true if function `pred` returns true for all elements of
323 Type: all :: (a -> bool) -> [a] -> bool
326 all (x: x < 3) [ 1 2 ]
328 all (x: x < 3) [ 1 2 3 ]
331 all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
333 /* Count how many elements of `list` match the supplied predicate
336 Type: count :: (a -> bool) -> [a] -> int
339 count (x: x == 3) [ 3 2 3 4 6 ]
344 pred: foldl' (c: x: if pred x then c + 1 else c) 0;
346 /* Return a singleton list or an empty list, depending on a boolean
347 value. Useful when building lists with optional elements
348 (e.g. `++ optional (system == "i686-linux") firefox`).
350 Type: optional :: bool -> a -> [a]
358 optional = cond: elem: if cond then [elem] else [];
360 /* Return a list or an empty list, depending on a boolean value.
362 Type: optionals :: bool -> [a] -> [a]
365 optionals true [ 2 3 ]
367 optionals false [ 2 3 ]
373 # List to return if condition is true
374 elems: if cond then elems else [];
377 /* If argument is a list, return it; else, wrap it in a singleton
378 list. If you're using this, you should almost certainly
379 reconsider if there isn't a more "well-typed" approach.
387 toList = x: if isList x then x else [x];
389 /* Return a list of integers from `first` up to and including `last`.
391 Type: range :: int -> int -> [int]
400 # First integer in the range
402 # Last integer in the range
407 genList (n: first + n) (last - first + 1);
409 /* Return a list with `n` copies of an element.
411 Type: replicate :: int -> a -> [a]
419 replicate = n: elem: genList (_: elem) n;
421 /* Splits the elements of a list in two lists, `right` and
422 `wrong`, depending on the evaluation of a predicate.
424 Type: (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; }
427 partition (x: x > 2) [ 5 1 2 3 4 ]
428 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
430 partition = builtins.partition or (pred:
433 then { right = [h] ++ t.right; wrong = t.wrong; }
434 else { right = t.right; wrong = [h] ++ t.wrong; }
435 ) { right = []; wrong = []; });
437 /* Splits the elements of a list into many lists, using the return value of a predicate.
438 Predicate should return a string which becomes keys of attrset `groupBy` returns.
440 `groupBy'` allows to customise the combining function and initial value
443 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
444 => { true = [ 5 3 4 ]; false = [ 1 2 ]; }
445 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";}
446 {name = "xfce"; script = "xfce4-session &";}
447 {name = "icewm"; script = "icewmbg &";}
448 {name = "mate"; script = "gnome-session &";}
450 => { icewm = [ { name = "icewm"; script = "icewm &"; }
451 { name = "icewm"; script = "icewmbg &"; } ];
452 mate = [ { name = "mate"; script = "gnome-session &"; } ];
453 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ];
456 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
457 => { true = 12; false = 3; }
459 groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
461 groupBy = builtins.groupBy or (
466 r // { ${key} = (r.${key} or []) ++ [e]; }
469 /* Merges two lists of the same size together. If the sizes aren't the same
470 the merging stops at the shortest. How both lists are merged is defined
471 by the first argument.
473 Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c]
476 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
480 # Function to zip elements of both lists
487 (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
489 /* Merges two lists of the same size together. If the sizes aren't the same
490 the merging stops at the shortest.
492 Type: zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }]
495 zipLists [ 1 2 ] [ "a" "b" ]
496 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
498 zipLists = zipListsWith (fst: snd: { inherit fst snd; });
500 /* Reverse the order of the elements of a list.
502 Type: reverseList :: [a] -> [a]
506 reverseList [ "b" "o" "j" ]
510 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
512 /* Depth-First Search (DFS) for lists `list != []`.
514 `before a b == true` means that `b` depends on `a` (there's an
515 edge from `b` to `a`).
518 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
519 == { minimal = "/"; # minimal element
520 visited = [ "/home/user" ]; # seen elements (in reverse order)
521 rest = [ "/home" "other" ]; # everything else
524 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
525 == { cycle = "/"; # cycle encountered at this element
526 loops = [ "/" ]; # and continues to these elements
527 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
528 rest = [ "/home" "other" ]; # everything else
531 listDfs = stopOnCycles: before: list:
533 dfs' = us: visited: rest:
535 c = filter (x: before x us) visited;
536 b = partition (x: before x us) rest;
537 in if stopOnCycles && (length c > 0)
538 then { cycle = us; loops = c; inherit visited rest; }
539 else if length b.right == 0
540 then # nothing is before us
541 { minimal = us; inherit visited rest; }
542 else # grab the first one before us and continue
545 (tail b.right ++ b.wrong);
546 in dfs' (head list) [] (tail list);
548 /* Sort a list based on a partial ordering using DFS. This
549 implementation is O(N^2), if your ordering is linear, use `sort`
552 `before a b == true` means that `b` should be after `a`
557 toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
558 == { result = [ "/" "/home" "/home/user" "other" ]; }
560 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
561 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
562 loops = [ "/" ]; } # loops back to these elements
564 toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
565 == { result = [ "other" "/" "/home" "/home/user" ]; }
567 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
570 toposort = before: list:
572 dfsthis = listDfs true before list;
573 toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
578 else if dfsthis ? cycle
579 then # there's a cycle, starting from the current vertex, return it
580 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
581 inherit (dfsthis) loops; }
582 else if toporest ? cycle
583 then # there's a cycle somewhere else in the graph, return it
585 # Slow, but short. Can be made a bit faster with an explicit stack.
586 else # there are no cycles
587 { result = [ dfsthis.minimal ] ++ toporest.result; };
589 /* Sort a list based on a comparator function which compares two
590 elements and returns true if the first argument is strictly below
591 the second argument. The returned list is sorted in an increasing
592 order. The implementation does a quick-sort.
595 sort (a: b: a < b) [ 5 3 7 ]
598 sort = builtins.sort or (
603 pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
606 else if strictLess first el
607 then next { inherit left; right = [ el ] ++ right; }
609 next { left = [ el ] ++ left; inherit right; };
610 pivot = pivot' 1 { left = []; right = []; };
613 else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
615 /* Compare two lists element-by-element.
618 compareLists compare [] []
620 compareLists compare [] [ "a" ]
622 compareLists compare [ "a" ] []
624 compareLists compare [ "a" "b" ] [ "a" "c" ]
627 compareLists = cmp: a: b:
634 else let rel = cmp (head a) (head b); in
636 then compareLists cmp (tail a) (tail b)
639 /* Sort list using "Natural sorting".
640 Numeric portions of strings are sorted in numeric order.
643 naturalSort ["disk11" "disk8" "disk100" "disk9"]
644 => ["disk8" "disk9" "disk11" "disk100"]
645 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"]
646 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"]
647 naturalSort ["v0.2" "v0.15" "v0.0.9"]
648 => [ "v0.0.9" "v0.2" "v0.15" ]
652 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
653 prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
654 less = a: b: (compareLists compare (head a) (head b)) < 0;
656 map (x: elemAt x 1) (sort less prepared);
658 /* Return the first (at most) N elements of a list.
660 Type: take :: int -> [a] -> [a]
663 take 2 [ "a" "b" "c" "d" ]
669 # Number of elements to take
670 count: sublist 0 count;
672 /* Remove the first (at most) N elements of a list.
674 Type: drop :: int -> [a] -> [a]
677 drop 2 [ "a" "b" "c" "d" ]
683 # Number of elements to drop
686 list: sublist count (length list) list;
688 /* Whether the first list is a prefix of the second list.
690 Type: hasPrefix :: [a] -> [a] -> bool
693 hasPrefix [ 1 2 ] [ 1 2 3 4 ]
695 hasPrefix [ 0 1 ] [ 1 2 3 4 ]
701 take (length list1) list2 == list1;
703 /* Remove the first list as a prefix from the second list.
704 Error if the first list isn't a prefix of the second list.
706 Type: removePrefix :: [a] -> [a] -> [a]
709 removePrefix [ 1 2 ] [ 1 2 3 4 ]
711 removePrefix [ 0 1 ] [ 1 2 3 4 ]
717 if hasPrefix list1 list2 then
718 drop (length list1) list2
720 throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument";
722 /* Return a list consisting of at most `count` elements of `list`,
723 starting at index `start`.
725 Type: sublist :: int -> int -> [a] -> [a]
728 sublist 1 3 [ "a" "b" "c" "d" "e" ]
734 # Index at which to start the sublist
736 # Number of elements to take
740 let len = length list; in
742 (n: elemAt list (n + start))
743 (if start >= len then 0
744 else if start + count > len then len - start
747 /* The common prefix of two lists.
749 Type: commonPrefix :: [a] -> [a] -> [a]
752 commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]
754 commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]
756 commonPrefix [ 1 2 3 ] [ 4 5 6 ]
763 # Zip the lists together into a list of booleans whether each element matches
764 matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
765 # Find the first index where the elements don't match,
766 # which will then also be the length of the common prefix.
767 # If all elements match, we fall back to the length of the zipped list,
768 # which is the same as the length of the smaller list.
769 commonPrefixLength = findFirstIndex id (length matchings) matchings;
771 take commonPrefixLength list1;
773 /* Return the last element of a list.
775 This function throws an error if the list is empty.
777 Type: last :: [a] -> a
784 assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
785 elemAt list (length list - 1);
787 /* Return all elements but the last.
789 This function throws an error if the list is empty.
791 Type: init :: [a] -> [a]
798 assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
799 take (length list - 1) list;
802 /* Return the image of the cross product of some lists by a function.
805 crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
806 => [ "13" "14" "23" "24" ]
808 crossLists = builtins.trace
809 "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
810 (f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
813 /* Remove duplicate elements from the list. O(n^2) complexity.
815 Type: unique :: [a] -> [a]
821 unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
823 /* Check if list contains only unique elements. O(n^2) complexity.
825 Type: allUnique :: [a] -> bool
828 allUnique [ 3 2 3 4 ]
830 allUnique [ 3 2 4 1 ]
833 allUnique = list: (length (unique list) == length list);
836 /* Intersects list 'e' and another list. O(nm) complexity.
839 intersectLists [ 1 2 3 ] [ 6 3 2 ]
842 intersectLists = e: filter (x: elem x e);
844 /* Subtracts list 'e' from another list. O(nm) complexity.
847 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
850 subtractLists = e: filter (x: !(elem x e));
852 /* Test if two lists have no common element.
853 It should be slightly more efficient than (intersectLists a b == [])
855 mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b);