简体   繁体   English

unique_copy元素值从map到vector

[英]unique_copy elements value from map to vector

I have a map<K, V> and I want to use unique_copy to put the values into a vector<V> . 我有一个map<K, V> ,我想使用unique_copy将值放入vector<V> I tried this but it doesn't work: 我试过这个,但它不起作用:

#include <iostream>
#include <map>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
using namespace placeholders;


int main() {
    map<std::string, int> mp;

    mp["1"] = 1;
    mp["2"] = 2;
    mp["3"] = 3;
    mp["4"] = 3;
    mp["5"] = 3;
    mp["6"] = 4;
    mp["7"] = 2;

    vector<int> vec;

    unique_copy( mp.begin(), mp.end(),
                 back_inserter(vec),
                 bind(&map<std::string, int>::value_type::second, _1) );


    for( auto& i : vec )
        cout<< i <<" ";
}

Expected output: 1 2 3 4 2 预期产量:1 2 3 4 2

All google searches return ways to use transform but I need unique_copy . 所有谷歌搜索都返回使用transform方法,但我需要unique_copy Is there any way to make this work? 有没有办法让这项工作?

There's no way to do this directly, because the value type of the map iterator and the vector iterator are not compatible. 没有办法直接执行此操作,因为map迭代器的值类型和向量迭代器不兼容。 You really need a transform of some sort, or an iterator adaptor. 你真的需要某种变换,或迭代器适配器。

You could use boost::transform_iterator for this: 你可以使用boost::transform_iterator

auto getValue = [](const std::map<std::string, int>::value_type &pair) { return pair.second; };

unique_copy(
  boost::make_transform_iterator(mp.begin(), getValue),
  boost::make_transform_iterator(mp.end(), getValue),
  back_inserter(vec)
);

If you cannot use Boost, you'll have to write such an iterator adaptor yourself: 如果你不能使用Boost,你必须自己编写这样一个迭代器适配器:

template <class T_PairIterator>
struct SecondIterator
{
  typedef T_PairIterator PairIterator;
  typedef typename std::iterator_traits<PairIterator>::iterator_category iterator_category;
  typedef typename std::iterator_traits<PairIterator>::value_type::second_type value_type;
  typedef typename std::iterator_traits<PairIterator>::difference_type difference_type;
  typedef value_type *pointer;
  typedef value_type &reference;

  PairIterator it;

  SecondIterator() {}

  explicit SecondIterator(PairIterator it) : it(it) {}

  pointer operator-> () const { return &it->second; }

  reference operator* () const { return it->second; }

  SecondIterator& operator++ () { ++it; return *this; }

  SecondIterator operator++ (int) { SecondIterator ret(*this); ++it; return ret; }
};

template <class T>
bool operator== (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return lhs.it == rhs.it; }

template <class T>
bool operator!= (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return !(lhs == rhs); }

template <class T>
SecondIterator<T> makeSecondIterator(const T &it)
{ return SecondIterator<T>(it); }

You could then use it like this: 然后你可以像这样使用它:

unique_copy(
  makeSecondIterator(mp.begin()),
  makeSecondIterator(mp.end()),
  back_inserter(vec)
);

Of course, the adaptor could be made a bit more generic (perhaps usable for first as well), and/or more encapsulated ( it needn't be public); 当然,可以使适配器更通用(也可以first使用),和/或更多封装( it不需要公开); it would also need proper handling for const-iterators. 它还需要对常量函数进行适当的处​​理。 But the above should be enough to give you the idea. 但上面应该足以让你有这个想法。

As Vijay says in his comment, you could use a std::set instead of a vector (if you don't need the individual features of a vector, of course, like contiguous storage, custom ordering or random access). 正如Vijay在他的评论中所说,你可以使用std::set而不是vector(如果你不需要向量的各个特征,当然,如连续存储,自定义排序或随机访问)。 In this case the std::set::insert will already take care of removing duplicates and you can use std::transform in the way you tried: 在这种情况下, std::set::insert将已经处理删除重复项,您可以按照您尝试的方式使用std::transform

std::set<int> set;
std::transform( mp.begin(), mp.end(), std::inserter(set),
                bind(&std::map<std::string, int>::value_type::second, _1) );

As to your modified question 至于你修改过的问题

std::set won't insert the value if it is already in the set but I don't want consecutive duplicates. std :: set不会插入值,如果它已经在集合中但我不想连续重复。

Well, in this case just do a normal copy (ie std::transform ) into the vector and do a std::unique afterwards, which will just remove all consecutive duplicates: 好吧,在这种情况下,只需在向量中执行正常的复制(即std::transform ),然后执行std::unique ,这将删除所有连续的重复项:

std::transform( mp.begin(), mp.end(), std::inserter(vec),
                bind(&std::map<std::string, int>::value_type::second, _1) );
vec.erase( std::unique(vec.begin(), vec.end()), vec.end() );

std::copy followed by vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() ); std::copy后跟vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() ); will end up in the same place as unique_copy with a transform would. 将与unique_copy结束在同一个地方并进行转换。 It may use extra memory in the middle (before removing redundant entries) but is pretty clean. 它可能在中间使用额外的内存(在删除冗余条目之前),但非常干净。

A manual copy is another option: 手动副本是另一种选择:

if(!src.empty()){
  auto* prev = &src.begin()->second;
  dest.push_back(*prev);
  for(auto it = std::next(src.begin()); it != sec.end(); ++it){
    if (it->second == *prev) continue;
    prev=&it->second;
    dest.push_back(*prev);
  }
}

with the note thay prev is technically redundant (use dest.back() instead of *prev except when push_back ing -- there use ->second ). 注意thay prev在技​​术上是多余的(使用dest.back()而不是*prev除非在push_back ing时 - 使用->second )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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