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()