简体   繁体   English

如何将copy_if从map映射到vector?

[英]How to copy_if from map to vector?

I'd like to copy values that match a predicate (equal ints) from a map<string,int> to a vector<int> . 我想将一个与谓词(等于整数)匹配的值从map<string,int>复制到vector<int>

This is what I tried: 这是我试过的:

#include <map>
#include <vector>
#include <algorithm>

int main()
{
    std::vector< int > v;
    std::map< std::string, int > m;

    m[ "1" ] = 1;
    m[ "2" ] = 2;
    m[ "3" ] = 3;
    m[ "4" ] = 4;
    m[ "5" ] = 5;

    std::copy_if( m.begin(), m.end(), v.begin(),
                  [] ( const std::pair< std::string,int > &it )
                  {
                    return ( 0 == ( it.second % 2 ) );
                  }
                  );
}

The error message from g++ 4.6.1 is : 来自g ++ 4.6.1的错误消息是:

error: cannot convert 'std::pair<const std::basic_string<char>, int>' to 'int' in assignment

Is there a way to adjust the example to do the above copy? 有没有办法调整示例来执行上述复制?

Problem 问题

The copy fails because you're copying from a map::iterator which iterates over pair<string const,int> to a vector::iterator which iterates over int . 复制失败是因为你要从map::iterator复制,它迭代pair<string const,int>到迭代intvector::iterator

Solution

Replace copy_if with for_each and do a push_back on your vector. copy_if替换为for_each并对向量执行push_back

Example

std::for_each( m.begin(), m.end(),
    [&v] ( std::pair< std::string const,int > const&it ) {
        if ( 0 == ( it.second % 2 ) ) {
            v.push_back(it.second);
        }
    }
);

With boost::range it is as easy as: 使用boost::range它很简单:

boost::push_back(
    v,
    m | boost::adaptors::map_values 
      | boost::adaptors::filtered([](int val){ return 0 == (val % 2); }));

The compiler error is actually quite succinct: 编译器错误实际上非常简洁:

error: cannot convert 'std::pair<const std::basic_string<char>, int>' to 'int' in assignment

And that's exactly what the problem is. 而这正是问题所在。 The map you're copying from has iterators that dereference to a pair<KEY,VALUE> , and there's no way to implicitly transform a pair<KEY,VALUE> to just a VALUE . 您要复制的map具有取消引用一pair<KEY,VALUE>迭代器,并且无法pair<KEY,VALUE>隐式转换VALUE

Because of this, you can't use copy or copy_if to copy from a map to a vector ; 因此,您无法使用copycopy_ifmap复制到vector ; but the Standard Library does provide an algorithm you can use, creatively called transform . 但标准库确实提供了一种可以使用的算法,创造性地称为transform transform is very similar to copy in that it takes two source iterators and a destination iterator. transformcopy非常相似,它需要两个源迭代器和一个目标迭代器。 The difference is transform also takes a unary function that does the actual transformation. 不同之处在于transform还需要一个实际转换的一元函数。 Using a C++11 lambda, you can copy the entire contents of a map to a vector like this: 使用C ++ 11 lambda,您可以将map的全部内容复制到这样的vector

transform( m.begin(), m.end(), back_inserter(v), [] (const MyMap::value_type& vt)
{
  return vt.second;
});

What if you don't want to copy the entire contents of the map , but only some elements meeting certian criteria? 如果您不想复制map的全部内容,但只有一些符合certian标准的元素,该怎么办? Simple, just use transform_if . 简单,只需使用transform_if

What's that, you say? 那是什么,你说? There is no transform_if in the Standard Library? 标准库中没有transform_if Well yeah, you do have a point there. 好吧,你确实有一点意见。 Frustratingly, there is no transform_if in the Standard Library. 令人沮丧的是,标准库中没有transform_if However writing one is a simple enough task. 写一个是一个足够简单的任务。 Here's the code: 这是代码:

template<class InputIterator, class OutputIterator, class UnaryFunction, class Predicate>
OutputIterator transform_if(InputIterator first, 
                            InputIterator last, 
                            OutputIterator result, 
                            UnaryFunction f, 
                            Predicate pred)
{
    for (; first != last; ++first)
    {
        if( pred(*first) )
            *result++ = f(*first);
    }
    return result; 
}

As you might expect, using transform_if is like taking copy_if and mashing it together with transform . 正如您所料,使用transform_if就像使用copy_if并将其与transform一起混搭。 Here's some psudo-code to demonstrate: 这里有一些psudo代码来演示:

transform_if( m.begin(), m.end(), back_inserter(v),
  [] (const MyMap::value_type& vt) // The UnaryFunction takes a pair<K,V> and returns a V
  {
    return vt.second;
  }, [] (const MyMap::value_type& vt) // The predicate returns true if this item should be copied
  {
     return 0 == (vt.second%2);
  } );

I cannot understand why the simple for loop solution is not the preferred approach, for this problem 对于这个问题,我无法理解为什么简单的for循环解决方案不是首选方法

for (std::map< std::string, int >::iterator it = m.begin(); it != m.end(); ++it )
{
   if ((it->second % 2) == 0)
      v.push_back(it->second);
}

Except that it makes the code more readable it performs better. 除了它使代码更具可读性,它表现更好。 I wrote a simple benchmark to see how a for loop performs compared to the other proposed solutions: 我写了一个简单的基准来看看for循环与其他提出的解决方案相比如何执行:

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
#include <sstream>

int main(int argc, char *argv[])
{
    std::map< std::string, int > m;
    std::vector<int> v;

    // Fill the map with random values...
    srand ( time(NULL) );

    for (unsigned i=0; i<10000; ++i)
    {
      int r = rand();
      std::stringstream out;
      out << r;
      std::string s = out.str();

      m[s] = r;
    } 

    /////////// FOR EACH ////////////////////

    clock_t start1 = clock();
    for (unsigned k=0; k<10000; k++)
    {
      v.clear();
      std::for_each( m.begin(), m.end(),
      [&v] ( const std::pair< std::string,int > &it ) {
      if ( 0 == ( it.second % 2 ) ) {
          v.push_back(it.second);
      }
      }
      );
    }
    clock_t end1=clock();
    std::cout << "Execution Time for_each : " << (end1-start1) << std::endl;

    /////////// TRANSFORM ////////////////////

    clock_t start2 = clock();
    for (unsigned k=0; k<10000; k++)
    {
      v.clear();
      std::transform(m.begin(), m.end(), std::back_inserter(v),
            [] ( const std::pair< std::string,int > &it )
            {
              return it.second;
            });
      v.erase(
    std::remove_if(
        v.begin(), v.end(), [](const int value){ return (value % 2) != 0; }),
    v.end());
    }
    clock_t end2 = clock();
    std::cout << "Execution Time transform : " << (end2-start2) << std::endl;


     /////////// SIMPLE FOR LOOP ////////////////////
    clock_t start3 = clock();
    for (unsigned k=0; k<10000; k++)
    {
      v.clear();
      for (std::map< std::string, int >::iterator it = m.begin(); it != m.end(); ++it )
      {
    if ((it->second % 2) == 0)
      v.push_back(it->second);
      }
    }
    clock_t end3=clock();
    std::cout << "Execution Time Simple For Loop : " << (end3-start3) << std::endl;

}

The results I got are the following: 我得到的结果如下:

Execution Time for_each : 7330000
Execution Time transform : 11090000
Execution Time Simple For Loop : 6530000

std::copy_if won't allow you to transfer from one type to another, only to filter what to copy. std::copy_if不允许您从一种类型转移到另一种类型,只是为了过滤要复制的内容。

You could use std::transform to get rid of the key and then use std::remove_if : 您可以使用std::transform来删除密钥,然后使用std::remove_if

  std::vector<int> v;
  std::transform(m.begin(), m.end(), std::back_inserter(v),
                  [] ( const std::pair< std::string,int > &it )
                  {
                    return it.second;
                  });
  v.erase(
      std::remove_if(
          v.begin(), v.end(), [](const int value){ return (value % 2) != 0; }),
      v.end());

However, a plain for loop would be more efficient and a lot easier to read. 但是,简单的for循环将更有效,更容易阅读。

Presumably you just want to retrieve the associated values from the map , not the keys. 大概你只想从map检索相关的值,而不是键。

The SGI version of STL has select1st and select2nd iterators for this kind of task. select1st版本的STL具有select1stselect2nd迭代器用于此类任务。

Personally, however, I don't think this should really be done with copy -- you're transforming the data, not copying it. 然而,就个人而言,我认为这不应该用复制完成 - 你正在转换数据,而不是复制数据。 As such, I'd advise using std::transform with a functor to return the second item in the pair. 因此,我建议使用带有仿函数的std::transform来返回对中的第二项。

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

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