Home > Mobile >  Easiest way to combine 3 std::vector into a temporary single std::vector?
Easiest way to combine 3 std::vector into a temporary single std::vector?

Time:11-07

I have seen this discussion (Concatenating two std::vectors) but it concerns combining (as in moving) two std::vector arrays.


I have three std::vectors and I am using C 17:

  1. m_mapHist[m_eHistAssign][strName]
  2. m_mapScheduleHist[m_eHistAssign][strName]
  3. m_mapScheduleFutureHist[m_eHistAssign][strName]

Each vector is of type std::vector<COleDateTime>. I don't want to change these vectors. Instead, I want to combine them (copy I guess) into a temporary single vector, so I can pass just the one vector to another class for processing.

At the moment I am doing this manually through iteration:

std::vector<COleDateTime> vecAssignmentDate;

// Past items from the history database
if (m_mapHist[m_eHistAssign].find(strName) != m_mapHist[m_eHistAssign].end())
{
    for (const auto& historyItemDate : m_mapHist[m_eHistAssign][strName])
    {
        vecAssignmentDate.push_back(historyItemDate);
    }
}

// Past items on the active schedule
if (m_mapScheduleHist[m_eHistAssign].find(strName) != m_mapScheduleHist[m_eHistAssign].end())
{
    for (const auto& historyItemDate : m_mapScheduleHist[m_eHistAssign][strName])
    {
        vecAssignmentDate.push_back(historyItemDate);
    }
}

// Future items (both on the active schedule and in the history database)
if (m_mapScheduleFutureHist[m_eHistAssign].find(strName) != m_mapScheduleFutureHist[m_eHistAssign].end())
{
    for(const auto &historyItemDate : m_mapScheduleFutureHist[m_eHistAssign][strName])
    {
        vecAssignmentDate.push_back(historyItemDate);
    }
}

Is there an easier way to create this temporary vector?

CodePudding user response:

You may use boost::join. Example:

std::vector<COleDateTime> v1;
std::vector<COleDateTime> v2;
std::vector<COleDateTime> v3;
std::vector<COleDateTime> result;

result = boost::join(boost::join(v1, v2), v3);

Since c 17 the standard also have a std::merge util function:

std::vector<COleDateTime> v1;
std::vector<COleDateTime> v2;
std::vector<COleDateTime> v3;
std::vector<COleDateTime> result;

std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
std::merge(v3.begin(), v3.end(), result.begin(), result.end(), std::back_inserter(result));

Differences between std::merge and std::copy

from cplusplus.com/reference/algorithm/merge/ std::merge:

Combines the elements in the sorted ranges [first1,last1) and [first2,last2), into a new range beginning at result with all its elements sorted.

from cplusplus.com/reference/algorithm/copy/ std::copy:

Copies the elements in the range [first,last) into the range beginning at result..

So it depends on what you want to achieve.

CodePudding user response:

Using a standard library, you can do this. BTW, I have not tested the code. It could contain errors.

#include <algorithm>
template<typename T, typename U, typename ...Args>
std::vector<COleDateTime> combine(T key1, U key2, Args ...arg) {
    std::vector<COleDateTime> ret;
    for (const auto& v : {arg...}) {
        if (v[key1].find(key2) != v[key1].cend()) {
            std::copy(v[key1][key2].begin(), v[key1][key2].end(), std::back_inserter(ret));
        }
    }
    return ret;
}

CodePudding user response:

What's wrong with a simple copy assignment?

const std::vector<COleDateTime>& v1;
const std::vector<COleDateTime>& v2;
const std::vector<COleDateTime>& v3;
std::vector<COleDateTime> result;

result.reserve(v1.size()   v2.size()   v3.size());
for(const std::vector<COleDateTime>* vec: {&v1, &v2, &v3})
    result.insert(result.end(), vec->begin(), vec->end());

Since your input vectors may not exist, here is a version that accounts for this:

using vector_type = std::vector<COleDateTime>;
using map_type = std::map<std::string, vector_type>;
auto find_or_null = [](const map_type& map, const std::string& key) noexcept
      -> const vector_type* {
    map_type::const_iterator found = map.find(key);
    return found == map.end() ? nullptr : &found->second;
};
const auto vecs = { find_or_null(n_mapHist, strName),
                    find_or_null(m_mapScheduleHist, strName),
                    find_or_null(m_mapScheduleFutureHist, strName)
};
std::size_t size = 0;
for(const vector_type* vec: vecs)
    if(vec)
        size  = vec->size();
vector_type result;
result.reserve(size);
for(const vector_type* vec: vecs)
    if(vec)
        result.insert(result.end(), vec->begin(), vec->end());
  • Related