【问题标题】:Easier way to count identical strings in a vector?计算向量中相同字符串的更简单方法?
【发布时间】:2013-04-18 21:29:38
【问题描述】:

我正在尝试获取向量中所有相同字符串的数量的输出,作为更大程序的一部分。经过大量研究,我设法将一些可行的东西放在一起,但看起来很乱,我想知道是否有更好的方法来做到这一点。

#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <iostream>

using namespace std;

void setMap(string i);
void addMap(string i);
map<string, int> myMap;

int main()
{
    vector<string> myVector;
    string myArray[6]={"foo","foo","bar","roo","foo","bar"};
    for (int i=0; i<6; i++)
    {
        myVector.push_back(myArray[i]);
    }
    for_each (myVector.begin(), myVector.end(), setMap);
    for_each (myVector.begin(), myVector.end(), addMap);
    for (map<string, int, less< string >>::const_iterator iter = myMap.begin();
      iter != myMap.end(); ++iter )
      cout <<iter->first<<'\t'<<iter->second<<endl;
    return 0;
}

void setMap(string i)
{
    myMap[i]=0;
}

void addMap(string i)
{
    myMap[i]++;
}

此代码运行良好,并为我提供了我所追求的输出,但我并不热衷于必须添加 2 个额外功能以使其工作或必须使地图成为全局。任何提示将不胜感激。

【问题讨论】:

  • 如果你想使用for_each,你可以使用一个functor来保存对你的地图的引用,从而消除你的大部分代码。 See it Live。您可以使用 lambda 使其更加更加紧凑。 See it live too.

标签: c++ string map vector


【解决方案1】:

没有额外功能和没有全局地图的最简单方法是不使用 for_each。

for_each (myVector.begin(), myVector.end(), setMap);
for_each (myVector.begin(), myVector.end(), addMap);

变成

map<string, int> myMap;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
    myMap[*i]=0;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
    ++myMap[*i];

完成后,您还可以删除第一个循环

map<string, int> myMap;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
    ++myMap[*i];

因为无论如何地图值都会被初始化为零。

是什么让您认为无论如何都必须使用 for_each?

【讨论】:

  • 对 C++ 非常陌生,实际上是一般的编程。这对我来说似乎很有意义,因为我在矢量方面没有太多经验并且以前从未使用过地图。我正在从事的项目是一个巨大的学习曲线,这个网站对我帮助很大。
  • for_each 只是 for 循环恕我直言的一个更尴尬的版本,我几乎从未发现自己写过一个。
  • 完美运行(除了 a , a ; 应该是,但我们都会打错字)。没有比这更好的解决方案了。
【解决方案2】:

您的setMap 函数是不必要的。

考虑一下这个函数的作用,如果地图的键不存在。

void addMap(string i)
{
    myMap[i]++;
}

表达式myMap[i] 将为您的地图添加一个新键。

由于值类型为int,这个新值将是int(),保证为0

【讨论】:

    【解决方案3】:

    这个呢?将计数机制封装在单独的函数中以实现可重用性。

    // Iterator pair based interface
    template <class Iterator>
    std::map<typename Iterator::value_type,int>
    count(Iterator begin, Iterator end) {
        std::map<typename Iterator::value_type,int> counts;
        for (Iterator i = begin; i != end; ++i)
            counts[*i]++;
        return counts;
    }
    
    // Sequence interface
    template <class Sequence>
    inline std::map<typename Sequence::value_type,int>
    count(Sequence seq) {
        return count(seq.begin(), seq.end());
    }
    

    然后像这样简单地使用它:

    // C++11
    for (const auto & c : count(myVector))
        cout << c->first << '\t' << c->second << endl;
    
    // C++03
    std::map<string,int> counts = count(myVector);
    for (std::map<string,int>::const_iterator c = counts.begin(), e = counts.end(); c != e; ++c)
        cout << c->first << '\t' << c->second << endl;
    

    Simple demo

    【讨论】:

    • 你需要template &lt;class Sequence&gt; std::map&lt;typename Sequence::value_type,int&gt; count(Sequence seq) { return count&lt;typename Sequence::value_type&gt;(seq.begin(),seq.end());}
    • 刚刚看到默认模板参数也是 C++11。该死,我已经习惯了;)
    【解决方案4】:

    在 C++11 下,你可以这样做:

    #include <string>
    #include <unordered_map>
    #include <iostream>
    
    int main() {
    
        std::string myArray[6] = {"foo","foo","bar","roo","foo","bar"};
    
        std::unordered_map<std::string, size_t> m;
        for (const auto& s : myArray)
            ++m[s];
    
        for (const auto& p : m)
            std::cout << p.first << "\t" << p.second << std::endl;
    
    }
    

    打印出来:

    foo     3
    bar     2
    roo     1
    

    这是可行的,因为m[s] 会自动将s 插入到m(如果还没有的话)。

    使用std::unordered_map(哈希表)可能比std::map(平衡树)便宜。


    你可以在 C++03 下做一些非常相似的事情,除了上面显示的“for each”循环将被常规的“for”循环替换。

    【讨论】:

      【解决方案5】:
      #include <iostream>
      #include <string>
      #include <vector>
      #include <iterator>
      #include <map>
      
      using namespace std;
      
      int main (int argc, char * const argv[]) {
      
          string myArray[]={"foo","foo","bar","roo","foo","bar"};
          int arr_length = 6;
          vector<string> myVector(myArray, myArray + arr_length);
      
          //Print contents of vector:
          copy(myVector.begin(), 
               myVector.end(), 
               ostream_iterator<string>(cout, " ")
          );
      
          cout << endl;
      
      
      
          map<string, int> myMap;
      
          vector<string>::iterator pos;
          for (pos=myVector.begin(); pos<myVector.end(); ++pos)
          {
              myMap[*pos] += 1;
          }
      
          map<string, int>::iterator mapPos;
          for (mapPos=myMap.begin(); mapPos != myMap.end(); ++mapPos) {
              cout << "word: " << mapPos->first << "\t"
                   << "count: " << mapPos->second << endl;
          }
      
      
      
      
              return 0;
      }
      
      
      --output:--
      foo foo bar roo foo bar 
      word: bar   count: 2
      word: foo   count: 3
      word: roo   count: 1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-31
        • 2021-05-01
        • 1970-01-01
        • 2011-08-17
        相关资源
        最近更新 更多