1# General list operations.
2
3{ lib }:
4let
5 inherit (lib.strings) toInt;
6 inherit (lib.trivial) compare min;
7 inherit (lib.attrsets) mapAttrs;
8in
9rec {
10
11 inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
12
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.
16
17 Type: singleton :: a -> [a]
18
19 Example:
20 singleton "foo"
21 => [ "foo" ]
22 */
23 singleton = x: [x];
24
25 /* Apply the function to each element in the list. Same as `map`, but arguments
26 flipped.
27
28 Type: forEach :: [a] -> (a -> b) -> [b]
29
30 Example:
31 forEach [ 1 2 ] (x:
32 toString x
33 )
34 => [ "1" "2" ]
35 */
36 forEach = xs: f: map f xs;
37
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))`.
41
42 Type: foldr :: (a -> b -> b) -> b -> [a] -> b
43
44 Example:
45 concat = foldr (a: b: a + b) "z"
46 concat [ "a" "b" "c" ]
47 => "abcz"
48 # different types
49 strange = foldr (int: str: toString (int + 1) + str) "a"
50 strange [ 1 2 3 4 ]
51 => "2345a"
52 */
53 foldr = op: nul: list:
54 let
55 len = length list;
56 fold' = n:
57 if n == len
58 then nul
59 else op (elemAt list n) (fold' (n + 1));
60 in fold' 0;
61
62 /* `fold` is an alias of `foldr` for historic reasons */
63 # FIXME(Profpatsch): deprecate?
64 fold = foldr;
65
66
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)`.
69
70 Type: foldl :: (b -> a -> b) -> b -> [a] -> b
71
72 Example:
73 lconcat = foldl (a: b: a + b) "z"
74 lconcat [ "a" "b" "c" ]
75 => "zabc"
76 # different types
77 lstrange = foldl (str: int: str + toString (int + 1)) "a"
78 lstrange [ 1 2 3 4 ]
79 => "a2345"
80 */
81 foldl = op: nul: list:
82 let
83 foldl' = n:
84 if n == -1
85 then nul
86 else op (foldl' (n - 1)) (elemAt list n);
87 in foldl' (length list - 1);
88
89 /* Strict version of `foldl`.
90
91 The difference is that evaluation is forced upon access. Usually used
92 with small whole results (in contrast with lazily-generated list or large
93 lists where only a part is consumed.)
94
95 Type: foldl' :: (b -> a -> b) -> b -> [a] -> b
96 */
97 foldl' = builtins.foldl' or foldl;
98
99 /* Map with index starting from 0
100
101 Type: imap0 :: (int -> a -> b) -> [a] -> [b]
102
103 Example:
104 imap0 (i: v: "${v}-${toString i}") ["a" "b"]
105 => [ "a-0" "b-1" ]
106 */
107 imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
108
109 /* Map with index starting from 1
110
111 Type: imap1 :: (int -> a -> b) -> [a] -> [b]
112
113 Example:
114 imap1 (i: v: "${v}-${toString i}") ["a" "b"]
115 => [ "a-1" "b-2" ]
116 */
117 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
118
119 /* Map and concatenate the result.
120
121 Type: concatMap :: (a -> [b]) -> [a] -> [b]
122
123 Example:
124 concatMap (x: [x] ++ ["z"]) ["a" "b"]
125 => [ "a" "z" "b" "z" ]
126 */
127 concatMap = builtins.concatMap or (f: list: concatLists (map f list));
128
129 /* Flatten the argument into a single list; that is, nested lists are
130 spliced into the top-level lists.
131
132 Example:
133 flatten [1 [2 [3] 4] 5]
134 => [1 2 3 4 5]
135 flatten 1
136 => [1]
137 */
138 flatten = x:
139 if isList x
140 then concatMap (y: flatten y) x
141 else [x];
142
143 /* Remove elements equal to 'e' from a list. Useful for buildInputs.
144
145 Type: remove :: a -> [a] -> [a]
146
147 Example:
148 remove 3 [ 1 3 4 3 ]
149 => [ 1 4 ]
150 */
151 remove =
152 # Element to remove from the list
153 e: filter (x: x != e);
154
155 /* Find the sole element in the list matching the specified
156 predicate, returns `default` if no such element exists, or
157 `multiple` if there are multiple matching elements.
158
159 Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a
160
161 Example:
162 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
163 => "multiple"
164 findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
165 => 3
166 findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
167 => "none"
168 */
169 findSingle =
170 # Predicate
171 pred:
172 # Default value to return if element was not found.
173 default:
174 # Default value to return if more than one element was found
175 multiple:
176 # Input list
177 list:
178 let found = filter pred list; len = length found;
179 in if len == 0 then default
180 else if len != 1 then multiple
181 else head found;
182
183 /* Find the first element in the list matching the specified
184 predicate or return `default` if no such element exists.
185
186 Type: findFirst :: (a -> bool) -> a -> [a] -> a
187
188 Example:
189 findFirst (x: x > 3) 7 [ 1 6 4 ]
190 => 6
191 findFirst (x: x > 9) 7 [ 1 6 4 ]
192 => 7
193 */
194 findFirst =
195 # Predicate
196 pred:
197 # Default value to return
198 default:
199 # Input list
200 list:
201 let found = filter pred list;
202 in if found == [] then default else head found;
203
204 /* Return true if function `pred` returns true for at least one
205 element of `list`.
206
207 Type: any :: (a -> bool) -> [a] -> bool
208
209 Example:
210 any isString [ 1 "a" { } ]
211 => true
212 any isString [ 1 { } ]
213 => false
214 */
215 any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
216
217 /* Return true if function `pred` returns true for all elements of
218 `list`.
219
220 Type: all :: (a -> bool) -> [a] -> bool
221
222 Example:
223 all (x: x < 3) [ 1 2 ]
224 => true
225 all (x: x < 3) [ 1 2 3 ]
226 => false
227 */
228 all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
229
230 /* Count how many elements of `list` match the supplied predicate
231 function.
232
233 Type: count :: (a -> bool) -> [a] -> int
234
235 Example:
236 count (x: x == 3) [ 3 2 3 4 6 ]
237 => 2
238 */
239 count =
240 # Predicate
241 pred: foldl' (c: x: if pred x then c + 1 else c) 0;
242
243 /* Return a singleton list or an empty list, depending on a boolean
244 value. Useful when building lists with optional elements
245 (e.g. `++ optional (system == "i686-linux") firefox`).
246
247 Type: optional :: bool -> a -> [a]
248
249 Example:
250 optional true "foo"
251 => [ "foo" ]
252 optional false "foo"
253 => [ ]
254 */
255 optional = cond: elem: if cond then [elem] else [];
256
257 /* Return a list or an empty list, depending on a boolean value.
258
259 Type: optionals :: bool -> [a] -> [a]
260
261 Example:
262 optionals true [ 2 3 ]
263 => [ 2 3 ]
264 optionals false [ 2 3 ]
265 => [ ]
266 */
267 optionals =
268 # Condition
269 cond:
270 # List to return if condition is true
271 elems: if cond then elems else [];
272
273
274 /* If argument is a list, return it; else, wrap it in a singleton
275 list. If you're using this, you should almost certainly
276 reconsider if there isn't a more "well-typed" approach.
277
278 Example:
279 toList [ 1 2 ]
280 => [ 1 2 ]
281 toList "hi"
282 => [ "hi "]
283 */
284 toList = x: if isList x then x else [x];
285
286 /* Return a list of integers from `first` up to and including `last`.
287
288 Type: range :: int -> int -> [int]
289
290 Example:
291 range 2 4
292 => [ 2 3 4 ]
293 range 3 2
294 => [ ]
295 */
296 range =
297 # First integer in the range
298 first:
299 # Last integer in the range
300 last:
301 if first > last then
302 []
303 else
304 genList (n: first + n) (last - first + 1);
305
306 /* Return a list with `n` copies of an element.
307
308 Type: replicate :: int -> a -> [a]
309
310 Example:
311 replicate 3 "a"
312 => [ "a" "a" "a" ]
313 replicate 2 true
314 => [ true true ]
315 */
316 replicate = n: elem: genList (_: elem) n;
317
318 /* Splits the elements of a list in two lists, `right` and
319 `wrong`, depending on the evaluation of a predicate.
320
321 Type: (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; }
322
323 Example:
324 partition (x: x > 2) [ 5 1 2 3 4 ]
325 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
326 */
327 partition = builtins.partition or (pred:
328 foldr (h: t:
329 if pred h
330 then { right = [h] ++ t.right; wrong = t.wrong; }
331 else { right = t.right; wrong = [h] ++ t.wrong; }
332 ) { right = []; wrong = []; });
333
334 /* Splits the elements of a list into many lists, using the return value of a predicate.
335 Predicate should return a string which becomes keys of attrset `groupBy` returns.
336
337 `groupBy'` allows to customise the combining function and initial value
338
339 Example:
340 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
341 => { true = [ 5 3 4 ]; false = [ 1 2 ]; }
342 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";}
343 {name = "xfce"; script = "xfce4-session &";}
344 {name = "icewm"; script = "icewmbg &";}
345 {name = "mate"; script = "gnome-session &";}
346 ]
347 => { icewm = [ { name = "icewm"; script = "icewm &"; }
348 { name = "icewm"; script = "icewmbg &"; } ];
349 mate = [ { name = "mate"; script = "gnome-session &"; } ];
350 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ];
351 }
352
353 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
354 => { true = 12; false = 3; }
355 */
356 groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
357
358 groupBy = builtins.groupBy or (
359 pred: foldl' (r: e:
360 let
361 key = pred e;
362 in
363 r // { ${key} = (r.${key} or []) ++ [e]; }
364 ) {});
365
366 /* Merges two lists of the same size together. If the sizes aren't the same
367 the merging stops at the shortest. How both lists are merged is defined
368 by the first argument.
369
370 Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c]
371
372 Example:
373 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
374 => ["he" "lo"]
375 */
376 zipListsWith =
377 # Function to zip elements of both lists
378 f:
379 # First list
380 fst:
381 # Second list
382 snd:
383 genList
384 (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
385
386 /* Merges two lists of the same size together. If the sizes aren't the same
387 the merging stops at the shortest.
388
389 Type: zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }]
390
391 Example:
392 zipLists [ 1 2 ] [ "a" "b" ]
393 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
394 */
395 zipLists = zipListsWith (fst: snd: { inherit fst snd; });
396
397 /* Reverse the order of the elements of a list.
398
399 Type: reverseList :: [a] -> [a]
400
401 Example:
402
403 reverseList [ "b" "o" "j" ]
404 => [ "j" "o" "b" ]
405 */
406 reverseList = xs:
407 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
408
409 /* Depth-First Search (DFS) for lists `list != []`.
410
411 `before a b == true` means that `b` depends on `a` (there's an
412 edge from `b` to `a`).
413
414 Example:
415 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
416 == { minimal = "/"; # minimal element
417 visited = [ "/home/user" ]; # seen elements (in reverse order)
418 rest = [ "/home" "other" ]; # everything else
419 }
420
421 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
422 == { cycle = "/"; # cycle encountered at this element
423 loops = [ "/" ]; # and continues to these elements
424 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
425 rest = [ "/home" "other" ]; # everything else
426
427 */
428 listDfs = stopOnCycles: before: list:
429 let
430 dfs' = us: visited: rest:
431 let
432 c = filter (x: before x us) visited;
433 b = partition (x: before x us) rest;
434 in if stopOnCycles && (length c > 0)
435 then { cycle = us; loops = c; inherit visited rest; }
436 else if length b.right == 0
437 then # nothing is before us
438 { minimal = us; inherit visited rest; }
439 else # grab the first one before us and continue
440 dfs' (head b.right)
441 ([ us ] ++ visited)
442 (tail b.right ++ b.wrong);
443 in dfs' (head list) [] (tail list);
444
445 /* Sort a list based on a partial ordering using DFS. This
446 implementation is O(N^2), if your ordering is linear, use `sort`
447 instead.
448
449 `before a b == true` means that `b` should be after `a`
450 in the result.
451
452 Example:
453
454 toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
455 == { result = [ "/" "/home" "/home/user" "other" ]; }
456
457 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
458 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
459 loops = [ "/" ]; } # loops back to these elements
460
461 toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
462 == { result = [ "other" "/" "/home" "/home/user" ]; }
463
464 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
465
466 */
467 toposort = before: list:
468 let
469 dfsthis = listDfs true before list;
470 toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
471 in
472 if length list < 2
473 then # finish
474 { result = list; }
475 else if dfsthis ? cycle
476 then # there's a cycle, starting from the current vertex, return it
477 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
478 inherit (dfsthis) loops; }
479 else if toporest ? cycle
480 then # there's a cycle somewhere else in the graph, return it
481 toporest
482 # Slow, but short. Can be made a bit faster with an explicit stack.
483 else # there are no cycles
484 { result = [ dfsthis.minimal ] ++ toporest.result; };
485
486 /* Sort a list based on a comparator function which compares two
487 elements and returns true if the first argument is strictly below
488 the second argument. The returned list is sorted in an increasing
489 order. The implementation does a quick-sort.
490
491 Example:
492 sort (a: b: a < b) [ 5 3 7 ]
493 => [ 3 5 7 ]
494 */
495 sort = builtins.sort or (
496 strictLess: list:
497 let
498 len = length list;
499 first = head list;
500 pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
501 if n == len
502 then acc
503 else if strictLess first el
504 then next { inherit left; right = [ el ] ++ right; }
505 else
506 next { left = [ el ] ++ left; inherit right; };
507 pivot = pivot' 1 { left = []; right = []; };
508 in
509 if len < 2 then list
510 else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
511
512 /* Compare two lists element-by-element.
513
514 Example:
515 compareLists compare [] []
516 => 0
517 compareLists compare [] [ "a" ]
518 => -1
519 compareLists compare [ "a" ] []
520 => 1
521 compareLists compare [ "a" "b" ] [ "a" "c" ]
522 => -1
523 */
524 compareLists = cmp: a: b:
525 if a == []
526 then if b == []
527 then 0
528 else -1
529 else if b == []
530 then 1
531 else let rel = cmp (head a) (head b); in
532 if rel == 0
533 then compareLists cmp (tail a) (tail b)
534 else rel;
535
536 /* Sort list using "Natural sorting".
537 Numeric portions of strings are sorted in numeric order.
538
539 Example:
540 naturalSort ["disk11" "disk8" "disk100" "disk9"]
541 => ["disk8" "disk9" "disk11" "disk100"]
542 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"]
543 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"]
544 naturalSort ["v0.2" "v0.15" "v0.0.9"]
545 => [ "v0.0.9" "v0.2" "v0.15" ]
546 */
547 naturalSort = lst:
548 let
549 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
550 prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
551 less = a: b: (compareLists compare (head a) (head b)) < 0;
552 in
553 map (x: elemAt x 1) (sort less prepared);
554
555 /* Return the first (at most) N elements of a list.
556
557 Type: take :: int -> [a] -> [a]
558
559 Example:
560 take 2 [ "a" "b" "c" "d" ]
561 => [ "a" "b" ]
562 take 2 [ ]
563 => [ ]
564 */
565 take =
566 # Number of elements to take
567 count: sublist 0 count;
568
569 /* Remove the first (at most) N elements of a list.
570
571 Type: drop :: int -> [a] -> [a]
572
573 Example:
574 drop 2 [ "a" "b" "c" "d" ]
575 => [ "c" "d" ]
576 drop 2 [ ]
577 => [ ]
578 */
579 drop =
580 # Number of elements to drop
581 count:
582 # Input list
583 list: sublist count (length list) list;
584
585 /* Return a list consisting of at most `count` elements of `list`,
586 starting at index `start`.
587
588 Type: sublist :: int -> int -> [a] -> [a]
589
590 Example:
591 sublist 1 3 [ "a" "b" "c" "d" "e" ]
592 => [ "b" "c" "d" ]
593 sublist 1 3 [ ]
594 => [ ]
595 */
596 sublist =
597 # Index at which to start the sublist
598 start:
599 # Number of elements to take
600 count:
601 # Input list
602 list:
603 let len = length list; in
604 genList
605 (n: elemAt list (n + start))
606 (if start >= len then 0
607 else if start + count > len then len - start
608 else count);
609
610 /* Return the last element of a list.
611
612 This function throws an error if the list is empty.
613
614 Type: last :: [a] -> a
615
616 Example:
617 last [ 1 2 3 ]
618 => 3
619 */
620 last = list:
621 assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
622 elemAt list (length list - 1);
623
624 /* Return all elements but the last.
625
626 This function throws an error if the list is empty.
627
628 Type: init :: [a] -> [a]
629
630 Example:
631 init [ 1 2 3 ]
632 => [ 1 2 ]
633 */
634 init = list:
635 assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
636 take (length list - 1) list;
637
638
639 /* Return the image of the cross product of some lists by a function.
640
641 Example:
642 crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
643 => [ "13" "14" "23" "24" ]
644 */
645 crossLists = builtins.trace
646 "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
647 (f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
648
649
650 /* Remove duplicate elements from the list. O(n^2) complexity.
651
652 Type: unique :: [a] -> [a]
653
654 Example:
655 unique [ 3 2 3 4 ]
656 => [ 3 2 4 ]
657 */
658 unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
659
660 /* Intersects list 'e' and another list. O(nm) complexity.
661
662 Example:
663 intersectLists [ 1 2 3 ] [ 6 3 2 ]
664 => [ 3 2 ]
665 */
666 intersectLists = e: filter (x: elem x e);
667
668 /* Subtracts list 'e' from another list. O(nm) complexity.
669
670 Example:
671 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
672 => [ 1 4 5 ]
673 */
674 subtractLists = e: filter (x: !(elem x e));
675
676 /* Test if two lists have no common element.
677 It should be slightly more efficient than (intersectLists a b == [])
678 */
679 mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b);
680
681}