简体   繁体   中英

Initialize empty vector of vectors with elements of vector of vectors

I have a function which shall initialize an empty vector of vectors from type string with certain elements from an given vector of vectors from type string. My syntax looks like this

std::vector<std::vector<std::string>> extract_data_on_userid(const std::vector<std::vector<std::string>> &array, std::vector<std::string> &user_ids, const int nr_of_events)
{
  std::vector<std::vector<std::string>> data_extract;
  int event_iterator = 0;
  int user_id_iterator = 0;

  // While loops which extracts the events based on user IDs
  while (event_iterator <= nr_of_events)
  {
    // While loop which finds specified user id in an event
    while (user_id_iterator < array[0].size())
    {
      if (check_id(user_ids, array[0][user_id_iterator]))
      {
        for (size_t i = 0; i < array.size(); i++)
        {
          data_extract[i].push_back(array[i][user_id_iterator]);
        }
      }
      user_id_iterator++;
    }

    event_iterator++;
  }

  return data_extract;
}

The given vector consists on varying number of string vectors (at least 2). My method shall search for certain UserIDs in

check_id(user_ids, array[0][user_id_iterator])

an then push the relevant event (user_id_iterator) in a new 2D Vector for all 1D Vectors

vector[i:in][user_id_iterator]

Into the newly initiated vector

std::vector<std::vector<std::string>> data_extract;

over the for loop.

 for (size_t i = 0; i < array.size(); i++)
    {
      data_extract[i].push_back(array[i][user_id_iterator]);
    }

It all does work as expected until the elements of the vectors[i:in] in row [user_id_iterator] shall be pushed into the emtpy vectors.

Do I intially have to initialize all 1D vectors in the 2D Vector data_extract? What is the correct syntax to fill an empty vector of vectors which certain elements from a filled vector of vectors? I receive an exception (segmentation fault) because the emtpy vector is not initialized correctly.

You made a simple error, which is easy to fix.

Your 2 dimensional vector

std::vector<std::vector<std::string>> data_extract;

is empty, after the definition. That means, it does not contain any element, in no dimension. Even not an element with an index [0] or [0][0] . So, you got a segfault, because you were accessing the element [i] , which is not existing.

So, yes, as you assumed, you have to initialize the vector. There are several possibilities. You can set the initial size using the std::vector constructor described here . Number 3) or 4)

So for example:

std::vector<std::vector<std::string>> data_extract(array.size());

Which is probably the most approriate solution.

You can also resize your vector after the definition

data_extract.resize(array.size());

but that is an additional line of code, not needed, because you can do in the constructor. You can of course also initialize both dimension of your vector, if you know the size of the second dimension.

std::vector<std::vector<std::string>> data_extract(array.size(),std::vector<std::string>(array[0].size(),""));

By the way. Your outer while loop is a no-op.

while (event_iterator <= nr_of_events)

The "event_iterator" is used nowhere in the the loop body and the inner while loops will never run. This, because in the 2nd round of the outer loop, "user_id_iterator" is already biiger than " array[0].size()" and the inner loop will never run.

Also the rest of the logic is hard to understand. I am not sure, why you always refer to "array[0]".

Your segmentation fault is hard to find, as you implemented your function way too complex. There are some simple refactoring measures, that reduce the noise that is generated here:

Calling a vector of string vectors array is realy irritating. Instead try renaming and aliasing it (not knowing your context exactly):

using TUserIdEvents = std::vector<std::vector<std::string>>;

TUserIdEvents extract_data_on_userid(const TUserIdEvents &eventsOfUserIds, std::vector<std::string> &user_ids, const int nr_of_events)
{
  TUserIdEvents data_extract;

Instead of using a while loop with the variable declaration and iteration outside, you can do it all in a single for loop:

// Loop which extracts the events based on user IDs  
for (int event_iterator = 0; event_iterator <= nr_of_events; ++event_iterator)

Your inner while can be replaced with a ranged based for loop, so you don't need to keep track of another int-iterator:

// Loop which finds specified user id in an event
for (const auto& userIdsOfEvent : eventsOfUserIds[0])
{
    if (check_id(user_ids, userIdsOfEvent))
    {
       for (size_t i = 0; i < eventsOfUserIds.size(); i++)
       {
          data_extract[i].push_back(userIdsOfEvent);
       }
    }
}

Which brings us to your actual problem:

data_extract[i].push_back(array[i][user_id_iterator]);

You are accessing data_extract with the iterator i , but data_extract is not initialized yet on the D1 level. To do that, you can construct it as follows:

TUserIdEvents data_extract(eventsOfUserIds.size());

This creates an amount of sub vectors within data_extract, equal to the amount of sub vectors passed as parameter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM