Code review
I agree with @papagaga that the name could be better, though I would choose perform_mappping. I just obfuscated name map, which is mostly thought of as std::map. I hope it will do more good than bad.
If the code doesn't have forwarding (or, as some call it, universal reference), don't use std::forward. In the case in question, there is none.
The code will generate a new function for each permutation of the types passed in. It might create a binary bloat if compiler will not be able to inline the function. Though, I believe in compilers. No conversions will ever happen, unless SFINAE kicks in.
With C++20's full templates on lambdas, the code might be able to hide the helper inside of itself, thus not exposing it.
Alternative
std::array and std::pair are tuples too, from the point of view of standard library. I thought "hey, lets make the function to work uniformly on every tuple by standard library". I thought it's gonna be easy ... unless I realized that std::array has non-type argument in it. This turned things to be verbose and not as elegant as I thought. Yet, the caller side is still good.
Implementation
The small problem is distinguishing tuples from non-tuples. std::tuple_size helps out with this. The harder one is identifying which type to return. Unfortunately I had to specialize it for tuple + pair and for array.
Code
#include <tuple>
#include <array>
#include <utility>
namespace details
{
    template <typename Tuple, typename Mapping>
    struct return_type;
    template <template <typename ...> typename Tuple, typename ... Types, typename Mapping>
    struct return_type<Tuple<Types...>, Mapping>
    {
        using type = Tuple<std::invoke_result_t<Mapping, Types>...>;
    };
    template <template <typename, std::size_t> typename Array, 
              typename T, std::size_t Size, typename Mapping>
    struct return_type<Array<T, Size>, Mapping>
    {
        using type = Array<std::invoke_result_t<Mapping, T>, Size>;
    };
    template <typename Tuple, typename Mapping>
    using return_type_t = typename return_type<Tuple, Mapping>::type;
    template <typename Tuple, typename Mapping, std::size_t ... Indices>
    return_type_t<std::decay_t<Tuple>, 
                  std::decay_t<Mapping>> perform_mapping(Tuple&& tup, 
                                                         Mapping&& mapping, 
                                                         std::index_sequence<Indices...>)
    {
        return {mapping(std::get<Indices>(std::forward<Tuple>(tup)))...};
    }
}
template <typename Tuple, typename Mapping, 
          std::size_t Size = std::tuple_size<std::decay_t<Tuple>>::value>
auto perform_mapping(Tuple&& tup, Mapping&& mapping)
{
    return details::perform_mapping(std::forward<Tuple>(tup), 
                                    std::forward<Mapping>(mapping), std::make_index_sequence<Size>{});
}
#include <algorithm>
#include <iterator>
#include <iostream>
#include <string>
int main()
{
    auto mapper = [](int x) {return x * 2;};
    std::array<int, 3> a{1, 2, 3};
    auto b = perform_mapping(a, mapper);
    std::copy(b.begin(), b.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
    auto tuple = std::make_tuple(1, std::string{"a"});
    auto self_adder = [](const auto& x) {return x + x;};
    auto another_tuple = perform_mapping(tuple, self_adder);
    std::cout << std::get<0>(another_tuple) << ' ' << std::get<1>(another_tuple) << '\n';
}
Demo.
In theory, if one specializes std::tuple_size for their own tuple (as long as it behaves like std::tuple or like std::array), it should simply work. 
For better or for worse, the new function allows mutation and side effects.