Какая в C++ альтернатива питоновскому enumerate?

118
26 мая 2019, 20:00

Какая в C++ есть альтернатива питоновскому enumerate, чтобы было что-то вроде этого:

# Для каждого элемента в массиве создаём массив с его индексами:
unique = [a, b, c]
array = [a,b,b,a,a,c,a,c,b,a,c,a]
indexes =[]
for j in unique:
    indexes.append([i+1 for i, e in enumerate(array) if e == j])
#indexes[0] = [1,4,5,7,10,12] для a
#indexes[1] = [2,3,9] для b
#indexes[2] = [6,8,11] для c

Как можно реализовать точно такой же код для вектора чисел (vector) в C++?

Answer 1

В стандартной библиотеке аналога enumerate нет, но можно сделать свой итератор/диапазон с аналогичной функциональностью. Подобные решения есть в Boost.Range и в range-v3.

В range-v3 это выглядит примерно так (один из вариантов):

#include <array>
#include <map>
#include <vector>
#include <range/v3/all.hpp>
auto foo(int a, int b, int c){
    namespace view = ranges::view;
    std::array unique = {a, b, c};
    std::array array = {a,b,b,a,a,c,a,c,b,a,c,a};
    std::map<int, std::vector<std::size_t>> indexes; // Это если опираться на то, что должно выводиться, у вас ожидаемый вывод не совпадает с кодом
    for(auto j: unique){
        for(auto&& [e, i] : view::zip(array, view::ints(1))){ //  аналог enunerate с индексацией от 1)
           if(j == e){
               indexes[j].push_back(i);
           }
        }
    }
    return indexes;
}
#include <iostream>
int main()
{
    int a = -5, b = 16, c = 81;
    ranges::copy(foo(a,b,c)[a], ranges::ostream_iterator<int>(std::cout, ", "));
    std::cout << "\n";
    ranges::copy(foo(a,b,c)[b], ranges::ostream_iterator<int>(std::cout, ", "));
    std::cout << "\n";
    ranges::copy(foo(a,b,c)[c], ranges::ostream_iterator<int>(std::cout, ", "));
    std::cout << "\n";
}

В онлайн компиляторе: ссылка

Upd:

Еще есть вариант с filter | transform, но filter считается устаревшим и заменён на remove_if, что менее наглядно (вместо добавления того, что хотим, удаляем то, что не нужно), а необходимость распаковки кортежей делает код довольно плохо читаемым.

И есть более-менее компромиссный вариант:

auto foo(int a, int b, int c){
    namespace view = ranges::view;
    std::array unique = {a, b, c};
    std::array array = {a,b,b,a,a,c,a,c,b,a,c,a};
    std::map<int, std::vector<std::size_t>> indexes; 
    for(auto j: unique){
        auto goalRange = view::zip(array, view::ints(1)) 
                         | view::for_each([&](auto&& pair){
                               auto [e, i] = pair;
                               return ranges::yield_if(e == j, i);
                           });
        ranges::copy(goalRange, ranges::back_inserter(indexes[j]));
    } 
    return indexes;
}
READ ALSO
Как изменить тип проекта в Visual Studio Community 2017?

Как изменить тип проекта в Visual Studio Community 2017?

При создании проекта выбрал пустой проект, тк

155
Для чего нужны данные типы?

Для чего нужны данные типы?

int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t

113
Почему методы класса меняются местами при использовании различных компиляторов?

Почему методы класса меняются местами при использовании различных компиляторов?

Имеется DLL C++ скомпилированная на MSVCПри динимической подгрузке DLL инициализируется фабрика и создает класс который используется дальше

112
Перевод целых и вещественный чисел в массив символов(char) без использования стандартных функций

Перевод целых и вещественный чисел в массив символов(char) без использования стандартных функций

Необходимо написать функции перевода вещественных и целых чисел в строку без функцийПервую функцию написал

124