Reputation: 1234
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 ?
Upvotes: 8
Views: 16516
Reputation: 15976
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;
}
}
Upvotes: 1
Reputation: 9715
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"}
.
Upvotes: 1
Reputation: 16421
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);
}
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));
}
Upvotes: 11