0

I have a vector of vectors and I want to take the contents of it into a single column vector. i.e.,

input: A = [[1 2] [3 4]]

output: v = [[1][2][3][4]] (column vector)

Is there a quick way of doing this in C++?

Wolfy
  • 548
  • 2
  • 9
  • 29
  • Possible duplicate of [Appending a vector to a vector](https://stackoverflow.com/questions/2551775/appending-a-vector-to-a-vector) – Killzone Kid Dec 20 '17 at 21:08
  • @KillzoneKid I do not think that post will help me in this case. – Wolfy Dec 20 '17 at 21:10
  • What do you mean by single column vector? A regular `std::vector` or a vector of vectors like `std::vector>` ending up having only one row / column? – Ron Dec 20 '17 at 21:12
  • `>>I do not think that post will help me in this case` This is exactly what you wanted and what you accepted as the answer, while in your ticket you described something different – Killzone Kid Dec 20 '17 at 21:25

4 Answers4

2

Like this:

std::vector<std::vector<int>> a = {{1, 2}, {3, 4}};
std::vector<std::vector<int>> b;
for ( auto& row : a )
{
   for ( auto item: row )
   {
      b.push_back(std::vector<int>{item});
   }
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • @KillzoneKid, I think the output is a vector of vectors where the inner vector consists of just one item. – R Sahu Dec 20 '17 at 21:18
1

You could std::copy every row using std::back_inserter like so:

int main()
{
    std::vector<std::vector<int>> v = {{1, 2, 3}, {2, 2, 3}};

    std::vector<int> b;

    for(auto& vec : v){
        std::copy(vec.begin(), vec.end(), std::back_inserter(b));
    }
}
Fureeish
  • 12,533
  • 4
  • 32
  • 62
1

If you want to copy all the elements from a vector of vectors into a single vector then utilize the two loops:

std::vector<std::vector<int>> vv = { { 1, 2, 3 },
                                     { 4, 5, 6 },
                                     { 7, 8, 9 } };
std::vector<int> v;
for (auto row : vv) {
    for (auto el : row) {
        v.push_back(el);
    }
}
//print out the vector:
for (auto el : v) {
    std::cout << el << ' ';
}

or utilize the std::copy function.

Ron
  • 14,674
  • 4
  • 34
  • 47
1

Unfortunately I have to tell you, that every other answer, at least until now, is not as good as it seems.

Let us step through the answers; in the end I tell you how to handle it properly.

std::vector<std::vector<int>> v = {{1, 2, 3}, {2, 2, 3}};
std::vector<int> b;
for(auto& vec : v){
    std::copy(vec.begin(), vec.end(), std::back_inserter(b)); // performes single insertion at the end
}

std::copy is bad style for inserting into a std::vector. You insert it value by value at the end of the destination vector. This means potentially more reallocations and moves/copies as needed.

std::vector<std::vector<int>> vv = { { 1, 2, 3 },
                                     { 4, 5, 6 },
                                     { 7, 8, 9 } };
std::vector<int> v;
for (auto row : vv) {
    for (auto el : row) {
        v.push_back(el);
    }
}

Same here. You resize it at every push_back, that is absolutely not necessary!

I recommend you the use of std::vector::insert. It performs some internal resizes by its own.

std::vector<std::vector<int>> v = {{1, 2, 3}, {2, 2, 3}};
std::vector<int> b;
for(auto& vec : v){
    b.insert(std::cend(b), std::cbegin(vec), std::cend(vec));
}

This solution performs a resize before any insertion occurs. This will result in the best possible performance.

Here some testcode. Try it by your own:

#include <vector>
#include <chrono>
#include <iostream>

int main()
{
    std::vector<int> v(100'000'000, 5);
    auto start = std::chrono::steady_clock::now();
    std::vector<int> b;
    b.insert(std::cend(b), std::cbegin(v), std::cend(v));
    auto end = std::chrono::steady_clock::now();
    std::cout << "insert durtion:\t" << (end - start).count() << std::endl;

    b = std::vector<int>();

    start = std::chrono::steady_clock::now();
    std::copy(std::cbegin(v), std::cend(v), std::back_inserter(b));
    end = std::chrono::steady_clock::now();
    std::cout << "copy durtion:\t" << (end - start).count() << std::endl;

    b = std::vector<int>();

    start = std::chrono::steady_clock::now();
    for (auto el : v)
        b.push_back(el);
    end = std::chrono::steady_clock::now();
    std::cout << "copy durtion:\t" << (end - start).count() << std::endl;
    std::cin.ignore();
    return 0;
}

This produces in x64 release in this output:

insert durtion:         132388657
copy durtion:           844505239
push_back durtion:      866565409

In the end, you could of course resize the vector first and then start the copy, but I think that's the wrong way to deal with that, if in fact, the std::vector already offers us this solution.

DNKpp
  • 259
  • 1
  • 7
  • single note: if you are already using `std::cend(b)`, you should also use `std::cbegin(vec)` and `std::cend(vec)` – Fureeish Dec 21 '17 at 00:05
  • @Fureeish You could. But then you are preventing the algorithm from moving the elements. But you are right, for this purpose the `std::cend/std::cbegin` is the better choice. – DNKpp Dec 21 '17 at 00:23
  • The elements won't ever be moved unless you use `std::move_iterator`, will they? – Fureeish Dec 21 '17 at 00:26