【问题标题】:How to get the smallest value group by variable value?如何通过变量值获得最小值组?
【发布时间】:2018-07-18 10:32:45
【问题描述】:

我是 cpp 中的菜鸟,我想获得一些帮助,我想选择包含这些对象列表的向量中的最小值。它是一种选择聚合

class Label{
private:
    std::string lbl;
    int n;

public:

    int getN() const { return this->n; }
    std::string getlbl() const { return this->lbl; }

};

int main() {
    std::vector<Label> my_vect = {
    {"labl07", 0}, {"labl07", 0}, {"labl07", 0}, 
    {"labl07", 0}, {"labl07", 0}, {"labl02", 232}, 
    {"labl02", 232}, {"labl02", 233}, {"labl02", 234}, 
    {"labl02", 230}, {"labl02", 233}, {"labl02", 234}, 
    {"labl02", 229}, {"labl03", 379}, {"labl03", 377}, 
    {"labl03", 379}, {"labl03", 381}, {"labl03", 380}, 
    {"labl03", 377}, {"labl03", 381}, {"labl03", 372}
    };

    for(auto & v: my_vect)
    {
        cout <<"dis : "<< v.getlbl() <<" value " <<  v.getN() << endl;

    }
    return 0;
}

我希望能做到这一点

dis : labl07 value 0
dis : labl02 value 229
dis : labl03 value 372

在下面的一些 cmets 中,他们使用地图关联容器我需要了解为什么而不是向量。

【问题讨论】:

  • std::sort 向量并且只打印第一个元素?如果您想消除重复,请在排序后应用std::unique...
  • 我不想删除重复项,我想获得该向量中每个标签名称的最小值
  • @cht_usr 然后不要申请std::unique,不过,排序和选择第一次出现的标签就可以了。如果您没有合适的operator &lt;,您可以提供一个自定义比较器作为 lambda:[](Label const&amp; x, Label const&amp; y) { return x.lbl &lt; y.lbl || x.lbl == y.lbl &amp;&amp; x.n &lt; y.n; }
  • 您是否依赖按向量中第一次出现的顺序列出标签?

标签: c++


【解决方案1】:

在这种情况下尝试使用关联容器映射,因为使用矢量更复杂。

string labelN;

string val;
int number;

map<string, int> values;  

while (readingInput)  
{
  // input next line
  fileInput >> labelN >>  " ">> val>> "value " >> number;
  if (number> values[val])
  {
     values[val] = number;
   }
}

在阅读了以下一些建议之后。我已经编写了这段代码,我认为它可以完成工作,除非有人写出更好的代码。因此,首先,您必须创建要添加到向量中的对象的构造函数。 其次,您必须添加一个函数,该函数将以聚合方式对向量进行排序,然后将结果插入到地图中。代码的最后一部分我将结果推送到向量中,您可能会使用它。

#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

class Label{
private:
    std::string lbl;
    int n;

public:
    Label(std::string sp, int np): lbl(sp), n(np) {}
    int getN() const { return this->n; }
    std::string getlbl() const { return this->lbl; }
    static bool sortByn( Label a, Label b )
    {
       if ( a.n < b.n ) return true;
       if ( a.n == b.n && a.lbl < b.lbl ) return true;
       return false;
    }

};

int main() {
    std::vector<Label> my_vect = {
    {"labl07", 0}, {"labl07", 0}, {"labl07", 0},
    {"labl07", 0}, {"labl07", 0}, {"labl02", 232},
    {"labl02", 232}, {"labl02", 233}, {"labl02", 234},
    {"labl02", 230}, {"labl02", 233}, {"labl02", 234},
    {"labl02", 229}, {"labl03", 379}, {"labl03", 377},
    {"labl03", 379}, {"labl03", 381}, {"labl03", 380},
    {"labl03", 377}, {"labl03", 381}, {"labl03", 372}
    };

    for(auto & v: my_vect)
    {
        cout <<"dis : "<< v.getlbl() <<" value " <<  v.getN() << endl;
    }

    map<string,int> smallest;
    string lbl;
    int n;

    for(auto & v: my_vect)
    {
        lbl = v.getlbl();
        n = v.getN();
        bool occurredBefore = smallest.count( lbl );
        if ( occurredBefore )
        {
         if ( n < smallest[lbl] ) smallest[lbl] = n;
        }
        else
        {
         smallest[lbl] = n;
        }
    }

   vector<Label> V;
   for ( auto e : smallest ) V.push_back( { e.first, e.second } );
   sort( V.begin(), V.end(), Label::sortByn );
   for ( Label L : V ) cout << L.getlbl() << '\t' << L.getN() << '\n';
}

【讨论】:

  • 1.想要的结果是最小的数字,你选择最大的。 2.values[val]将插入一个值为0的新对,如果没有输入负数,它将是最小值(提供1.已经固定)...你需要使用insert,如果第二个参数是false然后如果值更小,则更新第一个参数...
【解决方案2】:

正如@Aconcagua 所建议的,您可以使用自定义比较器对向量进行排序以对向量的值进行排序:

[](Label const& x, Label const& y) { 
            return ((x.getlbl() < y.getlbl()) || 
                   ((x.getlbl() == y.getlbl()) && (x.getN() < y.getN()))); };

您还需要一个构造函数来构造将插入向量中的对象:

Label(std::string label, int value) : lbl(label), n(value){}

当您遍历所有值时,只要标签不同,就打印元素。因此,代码可能如下所示:

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

class Label{
private:
    std::string lbl;
    int n;

public:
    Label(std::string label, int value) : lbl(label), n(value){}
    int getN() const { return this->n; }
    std::string getlbl() const { return this->lbl; }

};

int main() {
    std::vector<Label> my_vect = {
    {"labl07", 0}, {"labl07", 0}, {"labl07", 0}, 
    {"labl07", 0}, {"labl07", 0}, {"labl02", 232}, 
    {"labl02", 232}, {"labl02", 233}, {"labl02", 234}, 
    {"labl02", 230}, {"labl02", 233}, {"labl02", 234}, 
    {"labl02", 229}, {"labl03", 379}, {"labl03", 377}, 
    {"labl03", 379}, {"labl03", 381}, {"labl03", 380}, 
    {"labl03", 377}, {"labl03", 381}, {"labl03", 372}
    };

    std::sort(my_vect.begin(), my_vect.end(), [](Label const& x, Label const& y) { 
        return ((x.getlbl() < y.getlbl()) || ((x.getlbl() == y.getlbl()) && (x.getN() < y.getN()))); });

    std::string labelToPrint;

    for(const auto& v: my_vect)
    {
        if (labelToPrint.compare(v.getlbl()) != 0)
        {
            std::cout <<"dis : "<< v.getlbl() <<" value " <<  v.getN() << std::endl;    
            labelToPrint = v.getlbl();  
        }
    }
    return 0;
}

【讨论】:

  • 唯一的缺点:向量被修改,标签的输出不一定按照未排序向量中第一次出现的顺序(因此我的第二条评论要求相关性)。这可能是一个问题,也可能不是,但必须牢记。
【解决方案3】:

您可以使用 multimap 来执行此操作,请考虑以下示例(和 cmets)

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

struct x{
    std::string s_value;
    int i_value;
};

int main() {
    std::vector<x> v{
        {"01", 11},
        {"02", 9},
        {"03", 27},
        {"01", 3},
        {"02", 7},
        {"03", 34},
        {"01", 2},
        {"02", 6},
        {"03", 11},
    };
    // get unique keys
    std::vector<std::string> keys {};
    for(auto& x_value: v){
        // if key is not present in keys yet put it there
        if(std::find(keys.begin(),keys.end(), x_value.s_value) == keys.end()){
            keys.push_back(x_value.s_value);
        }
    }
    std::multimap<std::string, int> mmap;
    for(auto& x_value : v){
        //put values from vector into multimap
        mmap.insert( decltype(mmap)::value_type(x_value.s_value, x_value.i_value) );
    }

    for(auto& key : keys){
      // for each value we expect to be in multimap get range of values
      std::vector<int> values{};
      auto range = mmap.equal_range(key);
      // put vaules for range into vector
      for(auto i = range.first; i!= range.second; ++i){
          values.push_back(i->second);
      }
      // sort vector
      std::sort(values.begin(), values.end());
      // print the least value in range corresponding to key, if there was any
      if(!values.empty()){
        std::cout<<key<<" "<<values[0]<<std::endl;
      }
    }

    return 0;
}

【讨论】:

    【解决方案4】:

    虽然asdoudanswer 在技术上是正确的(指编辑,修订版 3),但它使用多个地图查找,可以通过以下变体避免:

    for(auto & v: my_vect)
    {
        int n = v.getN();
        // pre-C++11 variant:
        //auto entry = smallest.insert(std::make_pair(v.getlbl(), n));
        // since C++11:
        auto entry = smallest.emplace(v.getlbl(), n);
        if(!entry.second)
        {
            if(n < entry.first->second)
                entry.first->second = n;
        }
    }
    

    进一步的改进:字符串被复制,这实际上是没有必要的,因为地图的寿命并不比包含字符串的向量长。因此,如果 lbl 作为 const 引用返回,我们可以使用 std::reference_wrapper&lt;std::string&gt; 作为映射键(甚至可以使用 char const* 和适当的自定义比较器)。

    【讨论】:

    • emplace(..) 可能会替换 insert(std::make_pair(..))
    • @Jarod42 谢谢。即使经过这么多年努力保持最新状态,有时也会退回到 C++11 之前的时代......
    • 在 C++17 中,if (auto [it, inserted] = smallest.emplace(v.getlbl(), n); !inserted) { it-&gt;second = std::min(n, it-&gt;second);}
    • @Jarod42 啊,我记得作用域为 if 的变量,在等待编译器兼容时又忘记了它们......但是当然,这很酷!!!
    【解决方案5】:

    您可以使用range-v3 库轻松做到这一点:

    auto groups = my_vect | ranges::view::group_by(
       [](const Label& l1, const Label& l2){ return l1.getlbl() == l2.getlbl(); });
    
    for (const auto & group : groups) {
       auto min = ranges::min(group,
          [](const Label& l1, const Label& l2){ return l1.getN() < l2.getN(); });
    
       std::cout << min.getlbl() << ": " << min.getN() << std::endl;
    }
    

    输出:

    labl07: 0
    labl02: 229
    labl03: 372
    

    请注意,为了获得更高的性能 getlbl() 应该通过 const 引用返回。

    【讨论】:

    • 你甚至可以使用 projection 代替 lambda:ranges::min(group, std::less&lt;&gt;{}, &amp;Label::getN).
    • @Jarod42 即使group_by 视图也有一个投影选项会很好,比如我们可以写成| ranges::view::group_by(std::equal_to&lt;&gt;{}, &amp;Label::getlbl)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 2021-12-12
    • 2012-04-10
    • 2020-01-27
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多