Search code examples
c++templatesvectoridioms

Is there a more idiomatic way to split a std::vector?


I've written a function that splits a vector of size N into chunks of not more than size M.

So given a vector of size 47, and chunk size of 10, we get 5 chunks of size: 10,10,10,10,7.

template<typename T> std::vector<std::vector<T>> chunkVector(typename std::vector<T> source, typename std::vector<T>::size_type chunkSize)
{
    typename std::vector<std::vector<T>> returnVector;

    for (typename std::vector<T>::size_type i = 0; i < source.size(); i += chunkSize) {

        typename std::vector<T>::iterator start = source.begin() + i;
        typename std::vector<T>::iterator end = start + chunkSize > source.end() ? source.end() : start + chunkSize;
        typename std::vector<T> tempVector(start, end);

        returnVector.push_back(tempVector);
    }
    return returnVector;
}

Is there a more idiomatic way to do this? The requirement of random_access iterators suggests to me there might be a nicer way.


Solution

  • Only thing I would do differently is how to iterate, and how we're appending onto the result vector:

    template<typename T> 
    std::vector<std::vector<T>> chunkVector(const std::vector<T>& source, size_t chunkSize)
    {
        std::vector<std::vector<T>> result;
        result.reserve((source.size() + chunkSize - 1) / chunkSize);
    
        auto start = source.begin();
        auto end = source.end();
    
        while (start != end) {
            auto next = std::distance(start, end) >= chunkSize
                        ? start + chunkSize
                        : end;
    
            result.emplace_back(start, next);
            start = next;
        }
    
        return result;
    }