Reputation: 129
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.
Upvotes: 3
Views: 164
Reputation: 25516
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).
Upvotes: 1
Reputation: 1229
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';
}
Upvotes: 4
Reputation: 23497
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.
Upvotes: 0
Reputation: 2135
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;
}
Upvotes: 3
Reputation: 746
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;
}
Upvotes: 1