从std :: map的最后n个元素创建std :: vector的C ++惯用方法是什么?
我对保留向量中的顺序不感兴趣。
我可以复制元素,如下所示:
std::map< double, MyType > m;
size_t n = 3;
std::vector< MyType > v;
std::map< double, MyType >::iterator it = m.end();
while ( n-- ) { // assuming m.size() >= n
it--;
v.push_back(it->second);
}
但是,还有其他任何方式,更惯用吗?
答案 0 :(得分:5)
std::copy
将是合适的。但是,std::map<T,U>::iterator_type::value_type
不是U
(要复制的类型),而是std::pair<T,U>
(换句话说,解除引用映射迭代器会产生一对键和值类型),所以原始副本不起作用。
所以我们需要复制元素,沿途进行转换。这就是std::transform
的用途。
为方便起见,我将假设您的编译器支持C ++ 11 lambda表达式和auto
关键字。如果没有,它可以相当简单地重写为仿函数。但我们正在寻找类似的东西:
std::transform(map_first, map_last, std::back_inserter(vec), [](std::pair<double,MyType> p) { return p.second; });
现在我们只需要填写两个第一个参数:
auto map_first = std::next(map.end(), -n);
auto map_last = map.end();
这里唯一棘手的部分是地图迭代器是双向的,但不是随机访问,因此我们不能简单地说map.end() - n
。未定义-
运算符。相反,我们必须使用std::next
(对于双向运算符,它需要线性而不是恒定的时间,但是没有办法解决这个问题。)
(注意,我还没有尝试编译这段代码,因此可能需要进行一些调整)
答案 1 :(得分:4)
std::transform
将是最惯用的方式。你需要一个功能
对象:
template<typename PairType>
struct Second
{
typename PairType::second_type operator()( PairType const& obj ) const
{
return obj.second;
}
}
(如果你在std::map
或其他使用的东西上做了很多工作
std::pair
,您可以在工具箱中使用此功能。)
之后,它有点尴尬,因为你只想要最后一个n。以来 迭代器进入映射不是随机访问迭代器,而且无法添加 或减去任意值,最简单的解决方案是将它们全部复制, 然后删除你不想要的那些:
std::vector<MyType>
extractLastN( std::map<double, MyType> const& source, size_t n )
{
std::vector<MyType> results;
std::transform( source.begin(), source.end(),
std::back_inserter( results ),
Second<std::map<double, MyType>::value_type>() );
if ( results.size() > n ) {
results.erase( results.begin(), results.end() - n );
}
return results;
}
这不是最有效的,但取决于n
以及它的位置
使用,可能就足够了。如果你确实想避免额外的复制,
等等(仅当n
通常小于std::vector<MyType>
extractLastN( std::map<double, MyType> const& source, ptrdiff_t n )
{
std::map<double, MyType>::const_iterator start
= source.size() <= n
? source.begin()
: std::prev( source.end(), n );
std::vector<MyType> results;
std::transform( start, source.end(),
std::back_inserter( results ),
Second<std::map<double, MyType>::value_type>() );
return results;
}
时才值得
地图的大小),你必须做更好的事情:
std::prev
(如果您无权访问C ++ 11,template<typename IteratorType>
IteratorType
prev( IteratorType start, ptrdiff_t n )
{
std::advance( start, -n );
return start;
}
只是:
{{1}}
同样,如果你对标准库做了很多,你可能也是 已经在你的工具包里了。)
答案 2 :(得分:3)
这是一个简单的Boost.Range版本:
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/adaptor/map.hpp>
//#include <boost/range/adaptor/reversed.hpp> // comment in for 'reversed'
#include <map>
#include <vector>
struct X{};
int main(){
std::map<int, X> m;
unsigned n = 0;
auto vec(boost::copy_range<std::vector<X>>(
boost::make_iterator_range(m, m.size()-n, 0)
| boost::adaptors::map_values
//| boost::adaptors::reversed // comment in to copy in reverse order
));
}
答案 3 :(得分:2)
这样做的一种方法是使用简单的for_each
:
map<int,double> m;
vector<double> v;
//Fill map
auto siter = m.end();
advance(siter, -3);
for_each(siter, m.end(), [&](pair<int,double> p) { v.push_back(p.second); });
编辑更简单的方法是将std::prev
与for_each
一起使用:
map<int,double> m;
vector<double> v;
//Fill map
for_each(prev(m.end(), 3), m.end(),
[&](pair<int,double> p) { v.push_back(p.second); });
此外,如果您想以相反的顺序填充矢量,您可以使用:
for_each(m.rbegin(), next(m.rbegin(), 3),
[&](pair<int,double> p) { v.push_back(p.second); });
答案 4 :(得分:0)
向后做:
assert(n <= m.size());
std::copy(m.rbegin(), m.rbegin()+n, std::back_inserter(v));
双向迭代器FTW。
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
// I'm assuming it's ok to copy min(m.size(), n)
template <typename Iter>
Iter safe_advance(Iter begin, Iter const &end, int distance)
{
while(distance-- > 0 && begin != end)
++begin;
return begin;
}
void copy_last_n(map<double,int> const &m,
vector<int> &v, int n)
{
transform(m.rbegin(), safe_advance(m.rbegin(), m.rend(), n),
back_inserter(v),
[](map<double,int>::value_type const &val)
{
return val.second;
}
);
}
James Kanze已经编写了Second
仿函数 - 如果您无法访问lambdas,请使用它。
答案 5 :(得分:0)
首先,我们想写什么是惯用的?我建议:
std::vector<Mytype> v;
v.reserve(n);
std::transform(
limited(n, m.rbegin()),
limited_end(m.rend()),
std::back_inserter(v),
values_from(m)
);
现在我们只需要使代码有效。
我们可以用lambda(不需要values_from(m)
)替换m
,或者使用James Kanze的班级Second
来实现它(在这种情况下m
就在那里对于类型扣除):
template <typename Map>
Second<Map::value_type> values_from(const Map &) {
return Second<Map::value_type>();
}
实施limited
有点痛苦。它是一个返回迭代器的模板函数。它返回的迭代器类型是一个包装另一个迭代器类型的模板,并将所有内容转发给它,除了它跟踪n
,并且当它已经提前n
次时就像一个结束迭代器。 limited_end
返回相同类型的结束迭代器,因此如果底层迭代器相等则比较等于 ,或如果使用{创建其中一个迭代器{1}}而另一个已将limited_end
降至零。
我没有这个方便的代码,但它基本上是如何获得所有标准算法的n
等价物,而不仅仅是_n
。在这种情况下,我们需要copy_n
,但没有一个。
transform_n
的替代方法是使用transform
并copy_n
缠绕boost::transform_iterator
。我也不会在这里这样做,因为我总是要检查文档以使用m.rbegin()
。