Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ best way to split vector into n vector

Tags:

c++

c++11

vector

I have a std::vector<std::string> in this vector I push_back string from txt file, like this :

std::string line;
std::vector<std::string> path;
while(getline(fichier, line))
{    
   path.push_back(line);
}

I would like to split path vector into n other vector of 10 line for example. So if size of my vector is 25, I want 2 other vector of 10 element and one vector of 5 element.

What is the best way to do that ?

like image 732
simon Avatar asked Nov 17 '16 13:11

simon


3 Answers

Best is a matter of opinion, but you could do something like the following (with bunch_size being 10):

for(size_t i = 0; i < strings.size(); i += bunch_size) {
    auto last = std::min(strings.size(), i + bunch_size);
    bunches.emplace_back(strings.begin() + i, strings.begin() + last);
}

demo

If your strings are large and you want to avoid copying, you can go with the move version:

for(size_t i = 0; i < strings.size(); i += bunch_size) {
    auto last = std::min(strings.size(), i + bunch_size);
    auto index = i / bunch_size;
    auto& vec = bunches[index];
    vec.reserve(last - i);
    move(strings.begin() + i, strings.begin() + last, back_inserter(vec));
}

demo

like image 54
krzaq Avatar answered Sep 21 '22 06:09

krzaq


I propose something quite general (it works with different containers and different types, the complexity will change in that case):

#include <algorithm>
#include <iterator>
#include <vector>

template<typename Vector>
auto split_vector(const Vector& v, unsigned number_lines) {
  using Iterator = typename Vector::const_iterator;
  std::vector<Vector> rtn;
  Iterator it = v.cbegin();
  const Iterator end = v.cend();

  while (it != end) {
    Vector v;
    std::back_insert_iterator<Vector> inserter(v);
    const auto num_to_copy = std::min(static_cast<unsigned>(
        std::distance(it, end)), number_lines);
    std::copy(it, it + num_to_copy, inserter);
    rtn.push_back(std::move(v));
    std::advance(it, num_to_copy);
  }

  return rtn;
}

You can specify the number of lines you want to split:

For example:

int main(int argc, char *argv[]) {
  std::vector<std::string> input_vector = {"First", "Second", "Third"};
  auto vs = split_vector(input_vector, 2);
  return 0;
}

It will produce two vectors: {"First", "Second"} and {"Third"}.

like image 23
BiagioF Avatar answered Sep 24 '22 06:09

BiagioF


You may use stream iterators to do the job while reading the file:

using packet_t = Packet<5>;
using filler_t = std::istream_iterator<packet_t>;

std::vector<packet_t> packets{
    filler_t(stream),
    filler_t()
};

With the structure Packet declaring needed operator>>:

template<size_t size>
struct Packet
{
    std::vector<std::string> lines;

    friend std::istream& operator>>(std::istream& is, Packet& packet)
    {
        packet.lines.clear();
        std::string line;
        for(size_t i = 0; i < size && std::getline(is, line); ++i)
        {
            packet.lines.push_back(line);
        }
        if(packet.lines.size() > 0)
        {
            is.clear();
        }
        return is;
    }
};

Note that the stream is cleared when the packet is not empty for the last lines.

Complete code:

#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>

template<size_t size>
struct Packet
{
    std::vector<std::string> lines;

    friend std::istream& operator>>(std::istream& is, Packet& packet)
    {
        packet.lines.clear();
        std::string line;
        for(size_t i = 0; i < size && std::getline(is, line); ++i)
        {
            packet.lines.push_back(line);
        }
        if(packet.lines.size() > 0)
        {
            is.clear();
        }
        return is;
    }
};

int main()
{
    std::istringstream stream("1\n2\n3\n4\n5\n6\n7\n");

    using packet_t = Packet<5>;
    using filler_t = std::istream_iterator<packet_t>;

    std::vector<packet_t> packets{
        filler_t(stream),
        filler_t()
    };

    for(auto& packet : packets)
    {
        for(auto& line : packet.lines)
        {
            std::cout << line << "  ";
        }
        std::cout << std::endl;
    }
}
like image 27
rgmt Avatar answered Sep 21 '22 06:09

rgmt