From 981deeaa32b85a1374fbb37a47a3b78e07e45d14 Mon Sep 17 00:00:00 2001 From: Gammasoft Date: Sun, 29 Dec 2024 15:17:23 +0100 Subject: [PATCH] Add xtd::linq::enumerable::first_or_default methods and unit tests --- .../generic/extensions/enumerable.hpp | 14 +++ src/xtd.core/include/xtd/linq/enumerable.hpp | 80 ++++++++++++++++- .../src/xtd/linq/tests/enumerable_tests.cpp | 87 ++++++++++++++++++- 3 files changed, 178 insertions(+), 3 deletions(-) diff --git a/src/xtd.core/include/xtd/collections/generic/extensions/enumerable.hpp b/src/xtd.core/include/xtd/collections/generic/extensions/enumerable.hpp index 8639e12ea12d..7da4f500b437 100644 --- a/src/xtd.core/include/xtd/collections/generic/extensions/enumerable.hpp +++ b/src/xtd.core/include/xtd/collections/generic/extensions/enumerable.hpp @@ -165,6 +165,20 @@ namespace xtd { return xtd::linq::enumerable::average(base()); } + /// @brief Returns the first element of the sequence that satisfies a condition, or a specified default value if no such element is found. + /// @param predicate A function to test each element for a condition. + /// @param default_value The default value to return if the sequence is empty. + /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + source_t first_or_default(const std::function& predicate, const source_t& default_value) const noexcept { + return xtd::linq::enumerable::first_or_default(base(), predicate, default_value); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. + /// @param predicate A function to test each element for a condition. + /// @return default `source_t {}` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + source_t first_or_default(const std::function& predicate) const noexcept { + return xtd::linq::enumerable::first_or_default(base(), predicate); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. /// @param default_value The default value to return if the sequence is empty. /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. diff --git a/src/xtd.core/include/xtd/linq/enumerable.hpp b/src/xtd.core/include/xtd/linq/enumerable.hpp index 9f1a96c98ae2..95cdd258c29a 100644 --- a/src/xtd.core/include/xtd/linq/enumerable.hpp +++ b/src/xtd.core/include/xtd/linq/enumerable.hpp @@ -849,6 +849,82 @@ namespace xtd { /// @return The average of the sequence of values, or xtd::nullopt if the source sequence is empty or contains only values that are xtd::nullopt. static xtd::optional average(const xtd::collections::generic::ienumerable>& source) noexcept; + /// @brief Returns the first element of the sequence that satisfies a condition, or a specified default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @param default_value The default value to return if the sequence is empty. + /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static source_t first_or_default(const xtd::collections::generic::ienumerable& source, const std::function& predicate, const source_t& default_value) noexcept { + const auto& result = where(source, predicate); + return any(result) ? *result.begin() : default_value; + } + /// @brief Returns the first element of the sequence that satisfies a condition, or a specified default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @param default_value The default value to return if the sequence is empty. + /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static source_t first_or_default(std::initializer_list source, const std::function& predicate, const source_t& default_value) noexcept { + const auto& result = where(source, predicate); + return any(result) ? *result.begin() : default_value; + } + /// @brief Returns the first element of the sequence that satisfies a condition, or a specified default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @param default_value The default value to return if the sequence is empty. + /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static auto first_or_default(const collection_t& source, const std::function& predicate, const source_t& default_value) noexcept { + const auto& result = where(source, predicate); + return any(result) ? *result.begin() : default_value; + } + /// @brief Returns the first element of the sequence that satisfies a condition, or a specified default value if no such element is found. + /// @param first The first iterator. + /// @param last The last iterator. + /// @param predicate A function to test each element for a condition. + /// @param default_value The default value to return if the sequence is empty. + /// @return `default_value` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static auto first_or_default(input_iterator_t first, input_iterator_t last, const std::function& predicate, const source_t& default_value) noexcept { + const auto& result = where(first, last, predicate); + return any(result) ? *result.begin() : default_value; + } + + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @return default `source_t {}` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static source_t first_or_default(const xtd::collections::generic::ienumerable& source, const std::function& predicate) noexcept { + return first_or_default(source, predicate, source_t {}); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @return default `source_t {}` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static source_t first_or_default(std::initializer_list source, const std::function& predicate) noexcept { + return first_or_default(source, predicate, source_t {}); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. + /// @param source A sequence of values to return an element from. + /// @param predicate A function to test each element for a condition. + /// @return default `source_t {}` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static auto first_or_default(const collection_t& source, const std::function& predicate) noexcept { + return first_or_default(source, predicate, source_t {}); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. + /// @param first The first iterator. + /// @param last The last iterator. + /// @param predicate A function to test each element for a condition. + /// @return default `source_t {}` if source is empty or if no element passes the test specified by predicate; otherwise, the first element in source that passes the test specified by predicate. + template + static auto first_or_default(input_iterator_t first, input_iterator_t last, const std::function& predicate) noexcept { + return first_or_default(first, last, predicate, source_t {}); + } + /// @brief Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. /// @param source A sequence of values to return an element from. /// @param default_value The default value to return if the sequence is empty. @@ -914,7 +990,7 @@ namespace xtd { using source_t = typename std::decay::type; return first_or_default(first, last, source_t {}); } - + /// @brief Generates a sequence of integral numbers within a specified range. /// @param start The value of the first integer in the sequence. /// @param count The number of sequential integers to generate. @@ -1255,7 +1331,7 @@ namespace xtd { /// The following code example demonstrates how to use xtd::linq::enumerable::where (const xtd::collections::generic::ienumerable &, const std::function&) to filter a sequence. /// @include enumerable_where.cpp template - static const xtd::collections::generic::ienumerable& where(const collection_t& source, const std::function& selector) { + static const xtd::collections::generic::ienumerable& where(const collection_t& source, const std::function& predicate) { static thread_local auto result = enumerable_collection {}; result = enumerable_collection {}; for (const auto& item : source) diff --git a/tests/xtd.core.unit_tests/src/xtd/linq/tests/enumerable_tests.cpp b/tests/xtd.core.unit_tests/src/xtd/linq/tests/enumerable_tests.cpp index b718b552fe34..c392c5986dce 100644 --- a/tests/xtd.core.unit_tests/src/xtd/linq/tests/enumerable_tests.cpp +++ b/tests/xtd.core.unit_tests/src/xtd/linq/tests/enumerable_tests.cpp @@ -9,6 +9,7 @@ #include using namespace xtd; +using namespace xtd::collections::generic; using namespace xtd::linq; using namespace xtd::tunit; @@ -219,6 +220,90 @@ namespace xtd::linq::tests { assert::are_equal(nullopt, enumerable::average(array> {})); } + void test_method_(first_or_default_with_enumerable_predicate_and_default_value) { + assert::are_equal(3, enumerable::first_or_default(array {3, 4, 5}, [](int value) {return value <= 3;}, 2)); + assert::are_equal(2, enumerable::first_or_default(array {3, 4, 5}, [](int value) {return value < 3;}, 2)); + } + + void test_method_(first_or_default_with_initializer_list_predicate_and_default_value) { + assert::are_equal(3, enumerable::first_or_default({3, 4, 5}, [](int value) {return value <= 3;}, 2)); + assert::are_equal(2, enumerable::first_or_default({3, 4, 5}, [](int value) {return value < 3;}, 2)); + } + + void test_method_(first_or_default_with_collection_predicate_and_default_value) { + assert::are_equal(3, enumerable::first_or_default(std::vector {3, 4, 5}, [](int value) {return value <= 3;}, 2)); + assert::are_equal(2, enumerable::first_or_default(std::vector {3, 4, 5}, [](int value) {return value < 3;}, 2)); + } + + void test_method_(first_or_default_with_iterators_predicate_and_default_value) { + auto s = array {3, 4, 5}; + assert::are_equal(3, enumerable::first_or_default(s.begin(), s.end(), [](int value) {return value <= 3;}, 2)); + assert::are_equal(2, enumerable::first_or_default(s.begin(), s.end(), [](int value) {return value < 3;}, 2)); + } + + void test_method_(first_or_default_with_enumerable_and_predicate) { + assert::are_equal(3, enumerable::first_or_default(array {3, 4, 5}, [](int value) {return value <= 3;})); + assert::are_equal(0, enumerable::first_or_default(array {3, 4, 5}, [](int value) {return value < 3;})); + } + + void test_method_(first_or_default_with_initializer_list_and_predicate) { + assert::are_equal(3, enumerable::first_or_default({3, 4, 5}, [](int value) {return value <= 3;})); + assert::are_equal(0, enumerable::first_or_default({3, 4, 5}, [](int value) {return value < 3;})); + } + + void test_method_(first_or_default_with_collection_list_and_predicate) { + assert::are_equal(3, enumerable::first_or_default(std::vector {3, 4, 5}, [](int value) {return value <= 3;})); + assert::are_equal(0, enumerable::first_or_default(std::vector {3, 4, 5}, [](int value) {return value < 3;})); + } + + void test_method_(first_or_default_with_iterators_and_predicate) { + auto s = array {3, 4, 5}; + assert::are_equal(3, enumerable::first_or_default(s.begin(), s.end(), [](int value) {return value <= 3;})); + assert::are_equal(0, enumerable::first_or_default(s.begin(), s.end(), [](int value) {return value < 3;})); + } + + void test_method_(first_or_default_with_enumerable_and_default_value) { + assert::are_equal(3, enumerable::first_or_default(array {3, 4, 5}, 2)); + assert::are_equal(2, enumerable::first_or_default(array {}, 2)); + } + + void test_method_(first_or_default_with_initalizer_list_and_default_value) { + assert::are_equal(3, enumerable::first_or_default({3, 4, 5}, 2)); + assert::are_equal(2, enumerable::first_or_default(std::initializer_list {}, 2)); + } + + void test_method_(first_or_default_with_collection_and_default_value) { + assert::are_equal(3, enumerable::first_or_default(std::vector {3, 4, 5}, 2)); + assert::are_equal(2, enumerable::first_or_default(std::vector {}, 2)); + } + + void test_method_(first_or_default_with_iterators_and_default_value) { + auto s = array {3, 4, 5}; + assert::are_equal(3, enumerable::first_or_default(s.begin(), s.end(), 2)); + assert::are_equal(2, enumerable::first_or_default(s.begin(), s.begin(), 2)); + } + + void test_method_(first_or_default_with_enumerable) { + assert::are_equal(3, enumerable::first_or_default(array {3, 4, 5})); + assert::are_equal(0, enumerable::first_or_default(array {})); + } + + void test_method_(first_or_default_with_initializer_list) { + assert::are_equal(3, enumerable::first_or_default({3, 4, 5})); + assert::are_equal(0, enumerable::first_or_default(std::initializer_list {})); + } + + void test_method_(first_or_default_with_collection) { + assert::are_equal(3, enumerable::first_or_default(std::vector {3, 4, 5})); + assert::are_equal(0, enumerable::first_or_default(std::vector {})); + } + + void test_method_(first_or_default_with_iterators) { + auto s = array {3, 4, 5}; + assert::are_equal(3, enumerable::first_or_default(s.begin(), s.end())); + assert::are_equal(0, enumerable::first_or_default(s.begin(), s.begin())); + } + void test_method_(range_with_start_and_count) { collection_assert::are_equal({1, 2, 3, 4, 5}, enumerable::range(1, 5)); collection_assert::are_equal({11, 12, 13, 14, 15}, enumerable::range(11, 5)); @@ -228,7 +313,7 @@ namespace xtd::linq::tests { } void test_method_(to_list_with_enumerable) { - assert::is_instance_of>(enumerable::to_list(array {1, 2, 3})); + assert::is_instance_of>(enumerable::to_list(array {1, 2, 3})); collection_assert::are_equal({1, 2, 3}, enumerable::to_list(array {1, 2, 3})); }