简体   繁体   中英

How to get the smallest value group by variable value?

I'm noob in cpp I want to get some help, I want to select the lowest value within a vector which contain these list of object. it kind of select aggregation

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

I hope do this aggragation

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

in some comments below they use map associative container I need to understand why instead of vectors.

try to use associative container maps in this case, cause using vector is more complex.

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

after reading some advice below. I have written this code I think it does the job unless somebody writes a better one. So first, you have to create a constructor of the objects that ganna be added to your vector. Second, you have to add a function which will sort your vector in an aggregation way, then insert the result into the map. the last part of the code I pushed the results in vector you might use it.

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

As @Aconcagua has suggested, you can sort the vector using a custom comparator to sort the values of your vector:

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

You also need a constructor to construct the objects that will be inserted in the vector:

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

and when you iterate over all the values just print the element whenever the label is a different one. Thus, the code can look like:

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

you can use multimap to do this, consider following example (and comments)

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

While asdoud 's answer technically being correct (referring to edit, revision 3), it uses multiple map lookups which can be avoided by the following variant of:

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

Further improvement: Strings are yet copied, which actually is not necessary, as the map does not live longer than the vector, which contains the strings. So if lbl is returned as const reference, we could use std::reference_wrapper<std::string> as map keys (or even char const* with appropriate custom comparator).

You can do this easily with range-v3 library:

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

Output:

labl07: 0
labl02: 229
labl03: 372

Note that for higher performance getlbl() shoud return by const reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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