Skip to content

Commit

Permalink
Add xtd::linq::enumerable::first_or_default methods and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gammasoft71 committed Dec 29, 2024
1 parent 33b62a3 commit 981deea
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool(const source_t&)>& 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<bool(const source_t&)>& 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.
Expand Down
80 changes: 78 additions & 2 deletions src/xtd.core/include/xtd/linq/enumerable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<double> average(const xtd::collections::generic::ienumerable<xtd::optional<xtd::int64>>& 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 <typename source_t>
static source_t first_or_default(const xtd::collections::generic::ienumerable<source_t>& source, const std::function<bool(const source_t&)>& 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 <typename source_t>
static source_t first_or_default(std::initializer_list<source_t> source, const std::function<bool(const source_t&)>& 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 <typename source_t, typename collection_t>
static auto first_or_default(const collection_t& source, const std::function<bool(const source_t&)>& 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 <typename source_t, typename input_iterator_t>
static auto first_or_default(input_iterator_t first, input_iterator_t last, const std::function<bool(const source_t&)>& 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 <typename source_t>
static source_t first_or_default(const xtd::collections::generic::ienumerable<source_t>& source, const std::function<bool(const source_t&)>& 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 <typename source_t>
static source_t first_or_default(std::initializer_list<source_t> source, const std::function<bool(const source_t&)>& 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 <typename source_t, typename collection_t>
static auto first_or_default(const collection_t& source, const std::function<bool(const source_t&)>& 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 <typename source_t, typename input_iterator_t>
static auto first_or_default(input_iterator_t first, input_iterator_t last, const std::function<bool(const source_t&)>& 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.
Expand Down Expand Up @@ -914,7 +990,7 @@ namespace xtd {
using source_t = typename std::decay<decltype(*first)>::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.
Expand Down Expand Up @@ -1255,7 +1331,7 @@ namespace xtd {
/// The following code example demonstrates how to use xtd::linq::enumerable::where <source_t>(const xtd::collections::generic::ienumerable <source_t>&, const std::function<bool (const source_t&)>&) to filter a sequence.
/// @include enumerable_where.cpp
template<typename source_t, typename collection_t>
static const xtd::collections::generic::ienumerable<source_t>& where(const collection_t& source, const std::function<bool(const source_t&)>& selector) {
static const xtd::collections::generic::ienumerable<source_t>& where(const collection_t& source, const std::function<bool(const source_t&)>& predicate) {
static thread_local auto result = enumerable_collection<source_t> {};
result = enumerable_collection<source_t> {};
for (const auto& item : source)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <xtd/tunit/test_method_attribute>

using namespace xtd;
using namespace xtd::collections::generic;
using namespace xtd::linq;
using namespace xtd::tunit;

Expand Down Expand Up @@ -219,6 +220,90 @@ namespace xtd::linq::tests {
assert::are_equal(nullopt, enumerable::average(array<optional<int64>> {}));
}

void test_method_(first_or_default_with_enumerable_predicate_and_default_value) {
assert::are_equal(3, enumerable::first_or_default<int>(array {3, 4, 5}, [](int value) {return value <= 3;}, 2));
assert::are_equal(2, enumerable::first_or_default<int>(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<int>({3, 4, 5}, [](int value) {return value <= 3;}, 2));
assert::are_equal(2, enumerable::first_or_default<int>({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<int>(std::vector {3, 4, 5}, [](int value) {return value <= 3;}, 2));
assert::are_equal(2, enumerable::first_or_default<int>(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<int>(s.begin(), s.end(), [](int value) {return value <= 3;}, 2));
assert::are_equal(2, enumerable::first_or_default<int>(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<int>(array {3, 4, 5}, [](int value) {return value <= 3;}));
assert::are_equal(0, enumerable::first_or_default<int>(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<int>({3, 4, 5}, [](int value) {return value <= 3;}));
assert::are_equal(0, enumerable::first_or_default<int>({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<int>(std::vector {3, 4, 5}, [](int value) {return value <= 3;}));
assert::are_equal(0, enumerable::first_or_default<int>(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<int>(s.begin(), s.end(), [](int value) {return value <= 3;}));
assert::are_equal(0, enumerable::first_or_default<int>(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<int> {}, 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<int> {}, 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<int> {}, 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<int> {}));
}

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<int> {}));
}

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<int> {}));
}

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));
Expand All @@ -228,7 +313,7 @@ namespace xtd::linq::tests {
}

void test_method_(to_list_with_enumerable) {
assert::is_instance_of<xtd::collections::generic::list<int>>(enumerable::to_list(array {1, 2, 3}));
assert::is_instance_of<list<int>>(enumerable::to_list(array {1, 2, 3}));
collection_assert::are_equal({1, 2, 3}, enumerable::to_list(array {1, 2, 3}));
}

Expand Down

0 comments on commit 981deea

Please sign in to comment.