diff --git a/SimoxUtility/CMakeLists.txt b/SimoxUtility/CMakeLists.txt index 0b14327c3f7dd1cb08879188f5355e0c898668eb..8e5c50a6de8de92b71c932d438c71de064fda6c6 100644 --- a/SimoxUtility/CMakeLists.txt +++ b/SimoxUtility/CMakeLists.txt @@ -99,12 +99,13 @@ SET(INCLUDES preprocessor/variadic_for_each.h preprocessor/drop_front.h - algorithm/for_each_if.h + algorithm/advanced.h algorithm/apply.hpp + algorithm/contains.h + algorithm/for_each_if.h + algorithm/fuzzy_find.h algorithm/get_map_keys_values.h algorithm/minmax.h - algorithm/advanced.h - algorithm/fuzzy_find.h algorithm/string/string_tools.h algorithm/string/string_conversion.h algorithm/string/string_conversion_eigen.h diff --git a/SimoxUtility/algorithm.h b/SimoxUtility/algorithm.h index a4752d98709411b0351ec0376e155c5d794d88a6..bc7252a2cee84dc106b548b5a3e3025bdae0c14e 100644 --- a/SimoxUtility/algorithm.h +++ b/SimoxUtility/algorithm.h @@ -3,6 +3,7 @@ // This file is generated! #include "algorithm/advanced.h" +#include "algorithm/contains.h" #include "algorithm/for_each_if.h" #include "algorithm/fuzzy_find.h" #include "algorithm/get_map_keys_values.h" diff --git a/SimoxUtility/algorithm/contains.h b/SimoxUtility/algorithm/contains.h new file mode 100644 index 0000000000000000000000000000000000000000..45516257dff2a166ed516779558e4498ef93954c --- /dev/null +++ b/SimoxUtility/algorithm/contains.h @@ -0,0 +1,79 @@ +#pragma once + +#include <algorithm> +#include <functional> +#include <string> +#include <map> + + +namespace simox::alg +{ + + // For overloads of `simox::alg::contains` taking `std::string`: + // #include <SimoxUtility/algorithm/string/string_tools.h> + + // GENERAL CONTAINERS + + /** + * Return true if `value` is an element of `container`, false otherwise. + */ + template <class ContainerT, class ValueT> + bool contains(const ContainerT& container, const ValueT& value) + { + return std::find_if(container.begin(), container.end(), [&value](const auto& v) + { + return v == value; + }) != container.end(); + } + + /** + * Return true if `value` is an element of `container` (as indicated by `predicate`), + * false otherwise. + */ + template <class ContainerT, class ValueT, class PredicateT> + bool contains(const ContainerT& container, const ValueT& value, const PredicateT& predicate) + { + return std::find_if(container.begin(), container.end(), [&value, &predicate](const auto& v) + { + return predicate(v, value); + }) != container.end(); + } + + + // MAPS + + /** + * Return true if `key` is a key in `map`, false otherwise. + */ + template <class K, class V, template<class...> class MapT = std::map, class...Ts> + bool contains_key(const MapT<K, V, Ts...>& map, const K& key) + { + return map.count(key) > 0; + } + + /** + * Return true if `value` is a value in `map`, false otherwise. + */ + template <class K, class V, template<class...> class MapT = std::map, class...Ts> + bool contains_value(const MapT<K, V, Ts...>& map, const V& value) + { + return std::find_if(map.begin(), map.end(), [&value](const std::pair<K, V>& item) + { + return item.second == value; + }) != map.end(); + } + + + // Overloads for string literals (which otherwise don't match the previous definition). + template <class V, template<class...> class MapT = std::map, class...Ts> + bool contains_key(const MapT<std::string, V, Ts...>& map, const std::string& key) + { + return map.count(key) > 0; + } + template <class K, template<class...> class MapT = std::map, class...Ts> + bool contains_value(const MapT<K, std::string, Ts...>& map, const std::string& value) + { + return contains_value<K, std::string>(map, value); + } + +} diff --git a/SimoxUtility/algorithm/string/string_tools.h b/SimoxUtility/algorithm/string/string_tools.h index 327ec2516c1e7c2273850bed8bc2a70c8b783e2f..404f2c038daade083ec565f9be894ba151959b09 100644 --- a/SimoxUtility/algorithm/string/string_tools.h +++ b/SimoxUtility/algorithm/string/string_tools.h @@ -44,6 +44,7 @@ namespace simox::alg */ std::string capitalize_words(const std::string& str); + void trim(std::string& str, const std::locale& locale = DEFAULT_LOCALE); std::string trim_copy(const std::string& str, const std::locale& locale = DEFAULT_LOCALE); @@ -52,6 +53,7 @@ namespace simox::alg std::string trim_copy_if(const std::string& str, const std::string& trim = "\t "); + std::vector<std::string> split(const std::string& str, const std::string& splitBy = "\t ", bool trimElements = true, bool removeEmptyElements = true, const std::locale& locale = DEFAULT_LOCALE); @@ -62,17 +64,27 @@ namespace simox::alg std::string join(const std::vector<std::string> vec, const std::string& delimiter = " ", bool trimElements = false, bool ignoreEmptyElements = false, const std::locale& locale = DEFAULT_LOCALE); + std::string replace_first(std::string const& input, std::string const& search, std::string const& replace); std::string replace_last(std::string const& input, std::string const& search, std::string const& replace); std::string replace_all(std::string const& input, std::string const& search, std::string const& replace); + bool starts_with(std::string const& input, std::string const& search); bool ends_with(std::string const& input, std::string const& search); bool contains(const std::string& haystack, const std::string& needle); + inline bool contains(const std::string& haystack, const char* needle) + { + return contains(haystack, std::string(needle)); + } + inline bool contains(const std::string& string, const char character) + { + return string.find(character) != std::string::npos; + } template <typename IterT> diff --git a/SimoxUtility/tests/algorithm/CMakeLists.txt b/SimoxUtility/tests/algorithm/CMakeLists.txt index 6ec915c9b370c4725eb4a4d0aca3b4b5360ccb21..24614998a1993c6f9306581d40aea7f51d4b6afe 100644 --- a/SimoxUtility/tests/algorithm/CMakeLists.txt +++ b/SimoxUtility/tests/algorithm/CMakeLists.txt @@ -1,5 +1,6 @@ ADD_SU_TEST( apply ) +ADD_SU_TEST( contains ) ADD_SU_TEST( for_each_if ) ADD_SU_TEST( fuzzy_find ) ADD_SU_TEST( get_map_keys_values ) diff --git a/SimoxUtility/tests/algorithm/contains.cpp b/SimoxUtility/tests/algorithm/contains.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7c34cd83e6c2d3b6e4de38c8a99e7291d7b3ffe --- /dev/null +++ b/SimoxUtility/tests/algorithm/contains.cpp @@ -0,0 +1,172 @@ +/** +* @package SimoxUtility +* @author Rainer Kartmann +* @copyright 2021 Rainer Kartmann +*/ + +#define BOOST_TEST_MODULE SimoxUtility/algorithm/contains + +#include <boost/test/included/unit_test.hpp> + +#include <SimoxUtility/algorithm/contains.h> +#include <SimoxUtility/algorithm/string/string_tools.h> + +#include <map> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + + +namespace contains_test +{ + struct Fixture + { + const std::vector<std::string> string_vec { "one", "two", "three" }; + const std::vector<int> int_vec { 0, -1, 10 }; + + const std::set<std::string> string_set { string_vec.begin(), string_vec.end() }; + const std::set<int> int_set { int_vec.begin(), int_vec.end() }; + + const std::string string = "my/string"; + + + std::map<std::string, int> m_si; + std::map<std::string, std::string> m_ss; + std::map<int, std::string> m_is; + std::map<int, int> m_ii; + + std::unordered_map<std::string, int> um_si; + std::unordered_map<std::string, std::string> um_ss; + std::unordered_map<int, std::string> um_is; + std::unordered_map<int, int> um_ii; + + Fixture() + { + fillMap(m_si); + fillMap(m_ss); + fillMap(m_is); + fillMap(m_ii); + fillMap(um_si); + fillMap(um_ss); + fillMap(um_is); + fillMap(um_ii); + } + ~Fixture() + { + } + + template <class K, class V, template<class...> class MapT = std::map, class...Ts> + static MapT<K, V, Ts...> makeMap() + { + MapT<K, V, Ts...> map; + fillMap(map); + return map; + } + template <class K, class V, template<class...> class MapT = std::map, class...Ts> + static void fillMap(MapT<K, V, Ts...>& map) + { + for (int i = 1; i <= 3; ++i) + { + map[as<K>(i)] = as<V>(i); + } + } + + template <class T> + static T as(int i) + { + if constexpr(std::is_same_v<T, int>) + { + return i; + } + else if constexpr(std::is_same_v<T, std::string>) + { + return std::to_string(i); + } + } + }; +} + + +BOOST_FIXTURE_TEST_SUITE(contains_test, contains_test::Fixture) + + +BOOST_AUTO_TEST_CASE(test_contains) +{ + BOOST_CHECK_EQUAL(simox::alg::contains(string_vec, "two"), true); + BOOST_CHECK_EQUAL(simox::alg::contains(string_vec, "something"), false); + + BOOST_CHECK_EQUAL(simox::alg::contains(int_vec, -1), true); + BOOST_CHECK_EQUAL(simox::alg::contains(int_vec, 1000), false); + + BOOST_CHECK_EQUAL(simox::alg::contains(int_set, -1), true); + BOOST_CHECK_EQUAL(simox::alg::contains(int_set, 1000), false); + + BOOST_CHECK_EQUAL(simox::alg::contains(string_set, "two"), true); + BOOST_CHECK_EQUAL(simox::alg::contains(string_set, "something"), false); +} + + +BOOST_AUTO_TEST_CASE(test_string_contains) +{ + // Uses string_tools.h + // char + BOOST_CHECK_EQUAL(simox::alg::contains(string, '/'), true); + BOOST_CHECK_EQUAL(simox::alg::contains(string, ':'), false); + + // single-character string + BOOST_CHECK_EQUAL(simox::alg::contains(string, "/"), true); + BOOST_CHECK_EQUAL(simox::alg::contains(string, ":"), false); + + // multi-character string + BOOST_CHECK_EQUAL(simox::alg::contains(string, "my"), true); + BOOST_CHECK_EQUAL(simox::alg::contains(string, "your"), false); +} + + +BOOST_AUTO_TEST_CASE(test_map_contains) +{ + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_si, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_ss, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_si, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_ss, "2"), true); + + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_si, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_ss, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_si, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_ss, "something"), false); + + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_is, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_ii, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_is, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_ii, 2), true); + + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_is, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(m_ii, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_is, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_key(um_ii, -1), false); + + + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_is, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_ss, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_is, "2"), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_ss, "2"), true); + + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_is, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_ss, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_is, "something"), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_ss, "something"), false); + + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_si, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_ii, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_si, 2), true); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_ii, 2), true); + + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_si, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(m_ii, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_si, -1), false); + BOOST_CHECK_EQUAL(simox::alg::contains_value(um_ii, -1), false); +} + + +BOOST_AUTO_TEST_SUITE_END()