基于输入向量的向量重新排序向量
Reordering vector of vectors based on input vector
在一个小应用程序中,我一直使用std::vector
of std::vector<std::string>
临时存储
一些数据(从非 SQL 数据库中提取),然后再处理并将其上传到 SQL 数据库。很遗憾,
我从中提取数据的 API 不一定按查询指定的顺序排列 return 字段;
例如如果我的查询请求字段 x, y, z
,数据可能被 return 编辑为 y, x, z
,或 z, y, x
,等等......显然
这是有问题的,因为如果目标 SQL table 的列是 x, y, z
,那么插入的数据需要
反映这一点。
为了解释这种随机字段排序,我编写了一个小函数,它接受 (1) 输入数据,由 API 编辑 return;
(2) std::vector<std::string>
表示所需的列排序,如 SQL table 中所定义 - 和
相应地重新排序每个子向量的元素。由于输入数据的第一行是场向量
名称,我可以将它与正确排序的向量进行比较,并确定每个子向量应该如何
重新排序:
void fix_order(std::vector<std::vector<std::string>>& data, const std::vector<std::string>& correct) {
std::size_t width = data[0].size();
std::vector<int> order_idx(width);
for (std::size_t i = 0; i < width; i++) {
std::string tmp(data[0].at(i));
auto pos = std::find(correct.begin(), correct.end(), tmp);
order_idx[i] = std::distance(correct.begin(), pos);
}
for (std::size_t i = 0; i < data.size(); i++) {
if (!data[i].empty()) {
std::vector<std::string> q(width);
for (unsigned int j = 0; j < width; j++) {
int new_pos = order_idx[j];
q[new_pos] = data[i].at(j);
}
std::swap(data[i], q);
}
}
}
实际上,如果输入数据字段的顺序为 second, fourth, first, third
,并且我传递了一个指定正确顺序的向量 first, second, third, fourth
,则转换如下所示:
Before:
second fourth first third
2nd 4th 1st 3rd
2nd 4th 1st 3rd
After:
first second third fourth
1st 2nd 3rd 4th
1st 2nd 3rd 4th
虽然该函数产生了预期的结果,但我的循环和 STL 算法的混合感觉很草率,而且一般来说可读性不是很好。在其他情况下,我通常能够将 std::sort
与自定义比较器函数一起用于非标准排序,但我无法弄清楚如何在此处采用这种方法,其中 "sorting" 由预定义的输入,而不是某种类型的基于比较的逻辑。是否有更惯用的方法来实现这一点——即更好地利用 STL 算法(不一定 std::sort
)或其他 C++ 惯用语?
这里有一个 online demo 来重现这种情况。
如果转置数据,就像按向量中第一个元素的索引对向量进行排序一样简单。这将比您的解决方案慢,但可能更具可读性:
void fix_order(std::vector<std::vector<std::string>>& data, const std::vector<std::string>& correct) {
// setup index map, e.g. "first" --> 0
std::unordered_map<std::string, size_t> idx;
for (size_t i = 0; i < correct.size(); ++i) {
idx.insert(std::make_pair(correct[i], i));
}
// transpose for efficient sorting
auto tp = transpose(std::move(data));
// sort based on index map
std::sort(tp.begin(), tp.end(), [&](const std::vector<std::string>& lhs, const std::vector<std::string>& rhs){
return idx[lhs[0]] < idx[rhs[0]];
});
// transpose back to get the form you wanted
data = transpose(std::move(tp));
}
其中 transpose
只是:
std::vector<std::vector<std::string>> transpose(std::vector<std::vector<std::string>>&& data)
{
std::vector<std::vector<std::string>> result(data[0].size(),
std::vector<std::string>(data.size()));
for (size_t i = 0; i < data[0].size(); ++i) {
for (size_t j = 0; j < data.size(); ++j) {
result[i][j] = std::move(data[j][i]);
}
}
return result;
}
在一个小应用程序中,我一直使用std::vector
of std::vector<std::string>
临时存储
一些数据(从非 SQL 数据库中提取),然后再处理并将其上传到 SQL 数据库。很遗憾,
我从中提取数据的 API 不一定按查询指定的顺序排列 return 字段;
例如如果我的查询请求字段 x, y, z
,数据可能被 return 编辑为 y, x, z
,或 z, y, x
,等等......显然
这是有问题的,因为如果目标 SQL table 的列是 x, y, z
,那么插入的数据需要
反映这一点。
为了解释这种随机字段排序,我编写了一个小函数,它接受 (1) 输入数据,由 API 编辑 return;
(2) std::vector<std::string>
表示所需的列排序,如 SQL table 中所定义 - 和
相应地重新排序每个子向量的元素。由于输入数据的第一行是场向量
名称,我可以将它与正确排序的向量进行比较,并确定每个子向量应该如何
重新排序:
void fix_order(std::vector<std::vector<std::string>>& data, const std::vector<std::string>& correct) {
std::size_t width = data[0].size();
std::vector<int> order_idx(width);
for (std::size_t i = 0; i < width; i++) {
std::string tmp(data[0].at(i));
auto pos = std::find(correct.begin(), correct.end(), tmp);
order_idx[i] = std::distance(correct.begin(), pos);
}
for (std::size_t i = 0; i < data.size(); i++) {
if (!data[i].empty()) {
std::vector<std::string> q(width);
for (unsigned int j = 0; j < width; j++) {
int new_pos = order_idx[j];
q[new_pos] = data[i].at(j);
}
std::swap(data[i], q);
}
}
}
实际上,如果输入数据字段的顺序为 second, fourth, first, third
,并且我传递了一个指定正确顺序的向量 first, second, third, fourth
,则转换如下所示:
Before:
second fourth first third
2nd 4th 1st 3rd
2nd 4th 1st 3rd
After:
first second third fourth
1st 2nd 3rd 4th
1st 2nd 3rd 4th
虽然该函数产生了预期的结果,但我的循环和 STL 算法的混合感觉很草率,而且一般来说可读性不是很好。在其他情况下,我通常能够将 std::sort
与自定义比较器函数一起用于非标准排序,但我无法弄清楚如何在此处采用这种方法,其中 "sorting" 由预定义的输入,而不是某种类型的基于比较的逻辑。是否有更惯用的方法来实现这一点——即更好地利用 STL 算法(不一定 std::sort
)或其他 C++ 惯用语?
这里有一个 online demo 来重现这种情况。
如果转置数据,就像按向量中第一个元素的索引对向量进行排序一样简单。这将比您的解决方案慢,但可能更具可读性:
void fix_order(std::vector<std::vector<std::string>>& data, const std::vector<std::string>& correct) {
// setup index map, e.g. "first" --> 0
std::unordered_map<std::string, size_t> idx;
for (size_t i = 0; i < correct.size(); ++i) {
idx.insert(std::make_pair(correct[i], i));
}
// transpose for efficient sorting
auto tp = transpose(std::move(data));
// sort based on index map
std::sort(tp.begin(), tp.end(), [&](const std::vector<std::string>& lhs, const std::vector<std::string>& rhs){
return idx[lhs[0]] < idx[rhs[0]];
});
// transpose back to get the form you wanted
data = transpose(std::move(tp));
}
其中 transpose
只是:
std::vector<std::vector<std::string>> transpose(std::vector<std::vector<std::string>>&& data)
{
std::vector<std::vector<std::string>> result(data[0].size(),
std::vector<std::string>(data.size()));
for (size_t i = 0; i < data[0].size(); ++i) {
for (size_t j = 0; j < data.size(); ++j) {
result[i][j] = std::move(data[j][i]);
}
}
return result;
}