Search code examples
c++algorithmtemplatesc++17function-templates

Generic find_if to check an element in the 2D array


Using std::find_if we could find if an element exists in a normal one-dimensional arrays.

Inspired from this question, I was wondering, whether we can provide an algorithm function to check the exists in a 2d array(i.e. std::vector of std::vectors or std::array of std::array) of any types and any number of elements.

Is it possible to provide a generic function to find the existence of elements in a 2-dimensional array?

Something like:

template<typename Iterator>
auto isIn2DArray(
   Iterator begin, const Iterator end, ElementType ele) noexcept
{
   // returns the iterator pointing to the row, if the element is found, otherwise the end of the array!
}

Solution

  • With the help of the same std::find_if, we could achieve this.

    Following is a template function which takes the iterators (like in most of the standard algorithm functions) of the 2d array (either std::vector<std::vector<Type>>, or std::array<std::array<Type, RowSize>, ColSize>) and returns the iterator pointing to the inner-array(where the element exists), otherwise the end iterator of the 2d-array.

    (See Live Online)

    #include <type_traits>   // std::remove_reference_t, std::remove_const_t, std::conditional_t, std::is_fundamental_v
    #include <iterator>      // std::cbegin(), std::cend()
    #include <algorithm>     // std::find_if
    #include <utility>       // std::declval
    
    // traits for finding the inner element type of 2D array(of std::vector or std::array)
    template<typename Iterator>
    using ContainerType = std::remove_const_t<std::remove_reference_t<decltype(*std::declval<Iterator>())>>;
    
    template<typename Iterator>
    using ElementType = std::remove_const_t<std::remove_reference_t<typename ContainerType<Iterator>::value_type>>;
    
    template<typename Iterator>  // optional: ElementType<Iterator> should also be enough!
    using ElementArgumentType = std::conditional_t<std::is_fundamental_v<ElementType<Iterator>>
       , ElementType<Iterator>, ElementType<Iterator> const&>;
    
    template<typename Iterator>
    auto isIn2DArray(
       Iterator begin, const Iterator end, ElementArgumentType<Iterator> val) noexcept
    {
       // used the standard algorithm std::find_if here!
       return std::find_if(begin, end, [val](const auto& row) noexcept {
          return std::find_if(std::cbegin(row), std::cend(row), [val](const auto& element) noexcept {
             return element == val;
             }
          ) != std::cend(row);
          }
       );
    }
    

    Or pass the unary-predicate to the function which will be used for finding the appropriate array in an array of arrays. That will be less noisy!

    (See Live Online)

    #include <iterator>      // std::cbegin(), std::cend()
    #include <algorithm>     // std::find_if
    
    
    template<typename Iterator, typename UnaryPredicate>
    auto find_if_in_2DArray(
       Iterator begin, const Iterator end, UnaryPredicate unarayPred) noexcept
    {
       // used the standard algorithm std::find_if here!
       return std::find_if(begin, end, [unarayPred](const auto& row) noexcept {
          return std::find_if(std::cbegin(row), std::cend(row), unarayPred) != std::cend(row);
          }
       );
    }