seq = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
+
+ return ISequence.of(fnDistinctValues(seq));
+ }
+
+ /**
+ * Get the first occurrence of each distinct value in the provided list.
+ *
+ * Based on the XPath 3.1 fn:distinct-values
+ * function.
+ *
+ * @param values
+ * the items to get destinct values for
+ * @return a the list of distinct values
+ */
+ @NonNull
+ public static Stream fnDistinctValues(@NonNull List values) {
+ Set distinctValues = new TreeSet<>(FnDistinctValues::compare);
+ distinctValues.addAll(values);
+ return ObjectUtils.notNull(distinctValues.stream());
+ }
+
+ private static int compare(@NonNull IAnyAtomicItem item1, @NonNull IAnyAtomicItem item2) {
+ int retval;
+ try {
+ retval = item1.compareTo(item2);
+ } catch (InvalidTypeFunctionException | InvalidValueForCastFunctionException ex) {
+ retval = 1;
+ }
+ return retval;
+ }
+
+ private FnDistinctValues() {
+ // disable construction
+ }
+}
diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDistinctValuesTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDistinctValuesTest.java
new file mode 100644
index 000000000..49a3fc468
--- /dev/null
+++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDistinctValuesTest.java
@@ -0,0 +1,47 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.core.metapath.function.library;
+
+import static gov.nist.secauto.metaschema.core.metapath.TestUtils.decimal;
+import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer;
+import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence;
+import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase;
+import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
+import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+class FnDistinctValuesTest
+ extends ExpressionTestBase {
+ private static Stream provideValues() {
+ return Stream.of(
+ Arguments.of(
+ sequence(integer(1), decimal(2.0), integer(3)),
+ "fn:distinct-values((1, 2.0, 3, 2))"),
+ Arguments.of(
+ sequence(string("cherry"), string("plum")),
+ "fn:distinct-values((meta:string('cherry'),meta:string('plum'),meta:string('plum')))"),
+ Arguments.of(
+ sequence(string("a"), integer(2)),
+ "fn:distinct-values(('a', 2, 'a', 2.0))"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideValues")
+ void test(@NonNull ISequence> expected, @NonNull String metapath) {
+ assertEquals(expected, IMetapathExpression.compile(metapath)
+ .evaluate(null, newDynamicContext()));
+ }
+}