如何将_if从地图复制到矢量?

发布于 2024-12-15 15:38:50 字数 822 浏览 1 评论 0原文

我想将与谓词(等于整数)匹配的值从 map 复制到 vector

这是我尝试过的:

#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 ) );
                  }
                  );
}

g++ 4.6.1 的错误消息是:

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

有没有办法调整示例来执行上述复制?

I'd like to copy values that match a predicate (equal ints) from a map<string,int> to a 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 :

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

毁我热情 2024-12-22 15:38:50

使用 boost::range 就很简单:

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

With boost::range it is as easy as:

boost::push_back(
    v,
    m | boost::adaptors::map_values 
      | boost::adaptors::filtered([](int val){ return 0 == (val % 2); }));
要走干脆点 2024-12-22 15:38:50

问题

复制失败,因为您正在从迭代 pairmap::iterator 复制到 vector::iterator code> 迭代 int

解决方案

copy_if 替换为 for_each 并对向量执行 push_back

例子

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);
        }
    }
);

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.

Solution

Replace copy_if with for_each and do a push_back on your vector.

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);
        }
    }
);
灯角 2024-12-22 15:38:50

编译器错误实际上非常简洁:

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

这正是问题所在。您要复制的 map 具有取消引用 pair 的迭代器,并且无法隐式转换 < code>pairVALUE

因此,您无法使用 copycopy_ifmap 复制到 vector;但标准库确实提供了一种可以使用的算法,创造性地称为“变换”。 transformcopy 非常相似,因为它需要两个源迭代器和一个目标迭代器。不同之处在于 transform 还采用一个一元函数来执行实际的转换。使用 C++11 lambda,您可以将 map 的全部内容复制到 vector,如下所示:

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

如果您不想复制map,但只有一些元素满足某些标准?很简单,只需使用 transform_if 即可。

你说那是什么?标准库中没有transform_if?嗯,是的,你说的确实有道理。令人沮丧的是,标准库中没有 transform_if。然而,编写一个是一项足够简单的任务。代码如下:

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; 
}

正如您所料,使用 transform_if 就像采用 copy_if 并将其与 transform 混合在一起。下面是一些伪代码来演示:

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);
  } );

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.

Because of this, you can't use copy or copy_if to copy from a map to a vector; but the Standard Library does provide an algorithm you can use, creatively called transform. transform is very similar to copy in that it takes two source iterators and a destination iterator. The difference is transform also takes a unary function that does the actual transformation. Using a C++11 lambda, you can copy the entire contents of a map to a vector like this:

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? Simple, just use transform_if.

What's that, you say? There is no transform_if in the Standard Library? Well yeah, you do have a point there. Frustratingly, there is no transform_if in the Standard Library. 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. Here's some psudo-code to demonstrate:

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);
  } );
撕心裂肺的伤痛 2024-12-22 15:38:50

std::copy_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());

但是,普通的 for 循环会更高效并且更容易读。

std::copy_if won't allow you to transfer from one type to another, only to filter what to copy.

You could use std::transform to get rid of the key and then use 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.

轮廓§ 2024-12-22 15:38:50

我无法理解为什么简单的 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);
}

除了它使代码更具可读性之外,它的性能也更好。我编写了一个简单的基准测试来查看 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;

}

我得到的结果如下:

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

I cannot understand why the simple for loop solution is not the preferred approach, for this problem

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:

#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
妞丶爷亲个 2024-12-22 15:38:50

大概您只想从 map 中检索关联的值,而不是键。

STL 的 SGI 版本具有用于此类任务的 select1stselect2nd 迭代器。

然而,就我个人而言,我认为这实际上不应该通过复制来完成——您正在转换数据,而不是复制它。因此,我建议使用 std::transform 和函子来返回对中的第二项。

Presumably you just want to retrieve the associated values from the map, not the keys.

The SGI version of STL has select1st and select2nd iterators for this kind of task.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文