【发布时间】:2011-06-24 04:52:50
【问题描述】:
如果我有两个 stl 向量 vect1、vect2 并且我想从中生成一个映射,那么 vect1 中的第一个元素将对应于 vect2 中的第一个元素,依此类推。 我怎样才能以最简单的方式做到这一点?
【问题讨论】:
-
当它们的尺寸不同时,您希望发生什么?
如果我有两个 stl 向量 vect1、vect2 并且我想从中生成一个映射,那么 vect1 中的第一个元素将对应于 vect2 中的第一个元素,依此类推。 我怎样才能以最简单的方式做到这一点?
【问题讨论】:
这是一个使用标准库函数(和 C++0x lambdas)的解决方案。
const int data1[] = { 0, 2, 4, 6, 8 };
const int data2[] = { 1, 3, 5, 7, 9 };
std::vector<int> vec1(data1, data1 + 5);
std::vector<int> vec2(data2, data2 + 5);
std::map<int,int> map;
// create map
std::transform(vec1.begin(), vec1.end(), vec2.begin(), std::inserter(map, map.end()), [](int a, int b)
{
return std::make_pair(a, b);
});
// display map
std::for_each(map.begin(), map.end(), [](const std::pair<int,int>& p)
{
std::cout << p.first << "," << p.second << "\n";
});
注意:这里假设 vec1.size() 不大于 vec2.size()。
【讨论】:
std::transform(vec1.begin(), vec1.end(), vec2.begin(), std::inserter(my_map, my_map.end()), std::make_pair<int, std::string>);
std::make_pair 作为二进制函数对象。
std::bind 包装它,结果相同。 Lambdas 再次获胜。
std::vector<int> a, b;
// fill vectors here...
std::map<int, int> m;
assert(a.size() == b.size());
for (size_t i = 0; i < a.size(); ++i)
m[a[i]] = b[i];
【讨论】:
assert。没有人喜欢停止他们程序的库调用!
a 包含重复项时会发生什么?你可能想做if (m.find(a[i]) != m.end()) m.insert(a[i]) else throw ...。
insert 将返回一对,其中包含一个指向元素的迭代器和一个布尔值,指示它是否已插入或以前存在。无需进行之前的查找。
if (!m.insert(std::make_pair(a[i], b[i])).second) throw ...
我们将使用带有 2 个输入序列的 std::transform 版本。 (不像采用单个序列的那样广为人知)。
您可以将std::make_pair<v1::value_type, v2::value_type> 作为您的转换器(操作)传递给您
std::vector<int> vec1, vec2;
std::map< int, int > mergedMap;
std::transform( vec1.begin(), vec1.end(), vec2.begin(),
std::inserter(mergedMap, mergedMap.end() ), std::make_pair<int const&,int const&> );
我已经测试了代码,它可以在 GNU 4.3.2 上正常编译
(我现在也使用 C++11 进行了测试。当我将 make_pair 更改为采用 int const& 而不是 int 时,它可以工作)。
如果两个输入序列的长度不同,第一个较短就可以了,第二个序列中后面的元素将被忽略。如果第一个更长,它将产生未定义的行为。
【讨论】:
std::transform 算法,它接受两个序列和一个二进制函数对象。
std::transform 接受一个序列和一个输出迭代器(将写入转换后的结果)...
std::transform 算法有两种形式。
std::transform 发布了一个答案。我还作弊并使用了 C++0x lambda 表达式,因为它使整个事情变得如此简单。
这是一个使用 boost 的 zip_iterator 的细微变化
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/tuple/tuple.hpp>
// this is our map type
typedef std::map<int, std::string> map_t;
// this functor will be called for each "pair"
struct map_adder :
public std::unary_function<const boost::tuple<const int&, const std::string&>&, void>
{
map_adder(map_t& my_map) : _my_map(my_map){}
void operator()(const boost::tuple<const int&, const std::string&>& t) const
{
_my_map.insert(std::make_pair(t.get<0>(), t.get<1>()));
}
private:
mutable map_t& _my_map;
};
int main(void)
{
// test setup
std::vector<int> keys;
std::vector<std::string> values;
keys.push_back(1);
keys.push_back(2);
keys.push_back(3);
keys.push_back(4);
values.push_back("1");
values.push_back("2");
values.push_back("3");
values.push_back("4");
std::vector<int>::const_iterator beg1 = keys.begin();
std::vector<int>::const_iterator end1 = keys.end();
std::vector<std::string>::const_iterator beg2 = values.begin();
std::vector<std::string>::const_iterator end2 = values.end();
// destination
map_t my_map;
// functor to actually add
map_adder adder(my_map);
// simply iterate over...
std::for_each(
boost::make_zip_iterator(
boost::make_tuple(beg1, beg2)
),
boost::make_zip_iterator(
boost::make_tuple(end1, end2)
),
adder
);
std::cout << "size of map: " << my_map.size() << std::endl;
return 0;
}
好的,这是一个使用std::transform 的更简单 版本,我不知道已经存在的东西可以将boost::tuple 转换为std::pair 因此我的简单功能.. .
#include <iostream>
#include <algorithm>
#include <string>
#include <iterator>
#include <map>
#include <vector>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/tuple/tuple.hpp>
// this is our map type
typedef std::map<int, std::string> map_t;
map_t::value_type adapt_tuple(const boost::tuple<const map_t::key_type&, const map_t::mapped_type&>& t)
{
return map_t::value_type(t.get<0>(), t.get<1>());
}
int main(void)
{
std::vector<int> keys;
std::vector<std::string> values;
keys.push_back(1);
keys.push_back(2);
keys.push_back(3);
keys.push_back(4);
values.push_back("1");
values.push_back("2");
values.push_back("3");
values.push_back("4");
std::vector<int>::const_iterator beg1 = keys.begin();
std::vector<int>::const_iterator end1 = keys.end();
std::vector<std::string>::const_iterator beg2 = values.begin();
std::vector<std::string>::const_iterator end2 = values.end();
map_t my_map;
// simply iterate over...
std::transform(
boost::make_zip_iterator(
boost::make_tuple(beg1, beg2)
),
boost::make_zip_iterator(
boost::make_tuple(end1, end2)
),
std::inserter(my_map, my_map.end()),
adapt_tuple
);
std::cout << "size of map: " << my_map.size() << std::endl;
return 0;
}
【讨论】:
std::transform 版本,我唯一不知道的是已经可以将 boost::tuple 适应 std::pair 的东西,但乐趣在哪里在符合?? ;)
假设,您将忽略额外的(vect1 的大小!= vect2 的大小),这可能是一个解决方案:
map<T1, T2> target; //vector<T1> vect1, vector<T2> vect2;
vector<T1>::iterator it1 = vect1.begin();
vector<T2>::iterator it2 = vect2.begin();
while(it1 != vect1.end() && it2 != vect2.end())
{
target.insert(std::make_pair(*it1, *it2));
it1++;it2++;
}
编辑:感谢 Nim 指出 *it1 的事情。
【讨论】: