diff --git a/lib/default.nix b/lib/default.nix index d45e6de0c33f..875a1b9bb3ef 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -287,6 +287,7 @@ let init crossLists unique + uniqueStrings allUnique intersectLists subtractLists diff --git a/lib/lists.nix b/lib/lists.nix index ec0fe22d2afa..226e264176a0 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -11,7 +11,7 @@ let warn pipe ; - inherit (lib.attrsets) mapAttrs; + inherit (lib.attrsets) mapAttrs attrNames; inherit (lib) max; in rec { @@ -1839,6 +1839,10 @@ rec { /** Remove duplicate elements from the `list`. O(n^2) complexity. + :::{.note} + If the list only contains strings and order is not important, the complexity can be reduced to O(n log n) by using [`lib.lists.uniqueStrings`](#function-library-lib.lists.uniqueStrings) instead. + ::: + # Inputs `list` @@ -1864,6 +1868,43 @@ rec { */ unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ]; + /** + Removes duplicate strings from the `list`. O(n log n) complexity. + + :::{.note} + Order is not preserved. + + All elements of the list must be strings without context. + + This function fails when the list contains a non-string element or a [string with context](https://nix.dev/manual/nix/latest/language/string-context.html). + In that case use [`lib.lists.unique`](#function-library-lib.lists.unique) instead. + ::: + + # Inputs + + `list` + + : List of strings + + # Type + + ``` + uniqueStrings :: [ String ] -> [ String ] + ``` + + # Examples + :::{.example} + ## `lib.lists.uniqueStrings` usage example + + ```nix + uniqueStrings [ "foo" "bar" "foo" ] + => [ "bar" "foo" ] # order is not preserved + ``` + + ::: + */ + uniqueStrings = list: attrNames (groupBy id list); + /** Check if list contains only unique elements. O(n^2) complexity. diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index a723d198cb88..2662118a36e4 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -113,6 +113,7 @@ let toIntBase10 toShellVars types + uniqueStrings updateManyAttrsByPath versions xor @@ -1934,6 +1935,69 @@ runTests { expected = false; }; + testUniqueStrings_empty = { + expr = uniqueStrings [ ]; + expected = [ ]; + }; + testUniqueStrings_singles = { + expr = uniqueStrings [ + "all" + "unique" + "already" + ]; + expected = [ + "all" + "already" + "unique" + ]; + }; + testUniqueStrings_allDuplicates = { + expr = uniqueStrings [ + "dup" + "dup" + "dup" + ]; + expected = [ "dup" ]; + }; + testUniqueStrings_some_duplicates = { + expr = uniqueStrings [ + "foo" + "foo" + "bar" + "bar" + "baz" + ]; + expected = [ + "bar" + "baz" + "foo" + ]; + }; + testUniqueStrings_unicode = { + expr = uniqueStrings [ + "café" + "@" + "#" + "@" + "#" + "$" + "😎" + "😎" + "🙃" + "" + "" + ]; + expected = [ + "" + "#" + "$" + "@" + "café" + "😎" + "🙃" + ]; + }; + # ATTRSETS testConcatMapAttrs = {