Skip to content

Commit

Permalink
lib.lists.commonPrefix: init
Browse files Browse the repository at this point in the history
  • Loading branch information
infinisil committed Jul 20, 2023
1 parent 53dcfd2 commit 7f61b01
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/lists.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{ lib }:
let
inherit (lib.strings) toInt;
inherit (lib.trivial) compare min;
inherit (lib.trivial) compare min id;
inherit (lib.attrsets) mapAttrs;
in
rec {
Expand Down Expand Up @@ -663,6 +663,32 @@ rec {
else if start + count > len then len - start
else count);

/* The common prefix of two lists.
Type: commonPrefix :: [a] -> [a] -> [a]
Example:
commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]
=> [ 1 2 ]
commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]
=> [ 1 2 3 ]
commonPrefix [ 1 2 3 ] [ 4 5 6 ]
=> [ ]
*/
commonPrefix =
list1:
list2:
let
# Zip the lists together into a list of booleans whether each element matches
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
# Find the first index where the elements don't match,
# which will then also be the length of the common prefix.
# If all elements match, we fall back to the length of the zipped list,
# which is the same as the length of the smaller list.
commonPrefixLength = findFirstIndex id (length matchings) matchings;
in
take commonPrefixLength list1;

/* Return the last element of a list.
This function throws an error if the list is empty.
Expand Down
33 changes: 33 additions & 0 deletions lib/tests/misc.nix
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,39 @@ runTests {
expected = { a = [ 2 3 ]; b = [7]; c = [8];};
};

testListCommonPrefixExample1 = {
expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
expected = [ 1 2 ];
};
testListCommonPrefixExample2 = {
expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
expected = [ 1 2 3 ];
};
testListCommonPrefixExample3 = {
expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
expected = [ ];
};
testListCommonPrefixEmpty = {
expr = lists.commonPrefix [ ] [ 1 2 3 ];
expected = [ ];
};
testListCommonPrefixSame = {
expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
expected = [ 1 2 3 ];
};
testListCommonPrefixLazy = {
expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")];
expected = [ 1 ];
};
# This would stack overflow if `commonPrefix` were implemented using recursion
testListCommonPrefixLong =
let
longList = genList (n: n) 100000;
in {
expr = lists.commonPrefix longList longList;
expected = longList;
};

testSort = {
expr = sort builtins.lessThan [ 40 2 30 42 ];
expected = [2 30 40 42];
Expand Down

0 comments on commit 7f61b01

Please sign in to comment.