Is there a function similar to accummulate() but provides a unary pre-condition to filter the linear container when performing the operation? I search for accummulate_if but there isn't any. Thanks!
update:
Thanks for all the kind answers. I end up doing it this way:
std::for_each(v.begin(), v.end(), [&](int x){if (Pred) sum += x;});
Must you really use an algorithm?
Something as simple as below won't do?
for (const auto& v: V) if(pred(v)) sum+=v;
Sam's idea is also good. But I would do it with lambda:
sum = accumulate(
V.begin(), V.end(), 0,
[](int a, int b){return pred(b)? a+b: a;}
);
Pass your own binary op to std::accumulate():
#include <iostream>
#include <vector>
#include <numeric>
bool meets_criteria(int value) {
return value >= 5;
}
int my_conditional_binary_op(int a, int b) {
return meets_criteria(b) ? a + b : a;
}
class my_function_object {
private:
int threshold;
public:
my_function_object(int threshold) :
threshold(threshold) {
}
bool meets_criteria(int value) const {
return value >= threshold;
}
int operator()(int a, int b) const {
return meets_criteria(b) ? a + b : a;
}
};
int main() {
std::vector<int> v { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//sum [5...10] = 45
int sum;
sum = std::accumulate(v.begin(), v.end(), 0, my_conditional_binary_op);
std::cout << sum << std::endl;
//Use a function object to maintain states and additional parameters like 'threshold'
sum = std::accumulate(v.begin(), v.end(), 0, my_function_object(5));
std::cout << sum << std::endl;
return sum;
}
No, but you can write it yourself:
template
<
typename InputIterator,
typename AccumulateType,
typename BinaryOperation,
typename Predicate
>
const AccumulateType accumulate_if(
InputIterator first,
const InputIterator last,
AccumulateType init,
BinaryOperation&& binary_op,
Predicate&& predicate)
{
for (; first != last; ++first)
if (predicate(*first)) init = binary_op(init, *first);
return init;
}
Usage:
int main(int argc, char* argv[])
{
std::vector<int> v = {1,2,3,4,5};
std::cout << accumulate_if(v.begin(), v.end(), 0, std::plus<int>(), [] (int n) { return n > 3; });
return 0;
} // outputs 9
Related
I think I'm facing something that I imagine is a quite common problem here.
I'd like to write a function that would be able to accept both a container (let's say std::vector) of objects, and a container of pointers to those objects.
What would be the proper way to do so?
Right now, I'm thinking
int sum(std::vector<int *> v)
{
int s = 0;
for (int * i : v) s += *i;
return s;
}
int sum(std::vector<int> v)
{
std::vector<int *> vp;
for (size_t i = 0; i < v.size(); ++i)
vp[i] = &v[i];
return sum(vp);
}
But it doesn't seem quite right, does it?
Consider the standard algorithm library where the problem you see has a solution.
Most algorithms have some default behavior but often allow you to customize that behavior via functor parameters.
For your specific case the algorithm of choice is std::accumulate.
Because this algorithm already exists I can restrict to a rather simplified illustration here:
#include <iostream>
#include <functional>
template <typename T,typename R,typename F = std::plus<>>
R sum(const std::vector<T>& v,R init,F f = std::plus<>{})
{
for (auto& e : v) init = f(init,e);
return init;
}
int main() {
std::vector<int> x{1,2,3,4};
std::vector<int*> y;
for (auto& e : x ) y.push_back(&e);
std::cout << sum(x,0) << "\n";
std::cout << sum(y,0,[](auto a, auto b) {return a + *b;});
}
std::plus is a functor that adds two values. Because the return type may differ from the vectors element type an additional template parameter R is used. Similar to std::accumulate this is deduced from the initial value passed as parameter. When adding int the default std::plus<> is fine. When adding integers pointed to by pointers, the functor can add the accumulator with the dereferenced vector element. As already mentioned this is just a simple toy example. In the above link you can find a possible implementation of std::accumulate (which uses iterators rather than the container directly).
With C++20 (or another ranges library), you can easily add or remove pointerness
template <std::ranges::range R, typename T>
concept range_of = requires std::same<std::ranges::range_value_t<R>, T>;
template <range_of<int *> IntPointers>
int sum_pointers(IntPointers int_pointers)
{
int result = 0;
for (int * p : int_pointers) result += *p;
return result;
}
void call_adding_pointer()
{
std::vector<int> v;
sum_pointers(v | std::ranges::views::transform([](int & i){ return &i; });
}
Or
template <range_of<int> Ints>
int sum(Ints ints)
{
int result = 0;
for (int i : ints) result += i;
return result;
}
void call_removing_pointer()
{
std::vector<int *> v;
sum(v | std::ranges::views::transform([](int * p){ return *p; });
}
You can make a function template, which behaves differently for pointer and non-pointer:
#include <iostream>
#include <vector>
using namespace std;
template <class T>
auto sum(const std::vector<T> &vec)
{
if constexpr (std::is_pointer_v<T>)
{
typename std::remove_pointer<T>::type sum = 0;
for (const auto & value : vec) sum += *value;
return sum;
}
if constexpr (!std::is_pointer_v<T>)
{
T sum = 0;
for (const auto & value : vec) sum += value;
return sum;
}
}
int main(){
std::vector<int> a{3, 4, 5, 8, 10};
std::vector<int*> b{&a[0], &a[1], &a[2], &a[3], &a[4]};
cout << sum(a) << endl;
cout << sum(b) << endl;
}
https://godbolt.org/z/sch3KovaK
You can move almost everything out of the if constexpr to reduce code duplication:
template <class T>
auto sum(const std::vector<T> &vec)
{
typename std::remove_pointer<T>::type sum = 0;
for (const auto & value : vec)
{
if constexpr (std::is_pointer_v<T>)
sum += *value;
if constexpr (!std::is_pointer_v<T>)
sum += value;
}
return sum;
}
https://godbolt.org/z/rvqK89sEK
Based on #mch solution:
template<typename T>
std::array<double, 3> center(const std::vector<T> & particles)
{
if (particles.empty())
return {0, 0, 0};
std::array<double, 3> cumsum = {0, 0, 0};
if constexpr (std::is_pointer_v<T>)
{
for (const auto p : particles)
{
cumsum[0] += p->getX();
cumsum[1] += p->getY();
cumsum[2] += p->getZ();
}
}
if constexpr (not std::is_pointer_v<T>)
{
for (const auto p : particles)
{
cumsum[0] += p.getX();
cumsum[1] += p.getY();
cumsum[2] += p.getZ();
}
}
double f = 1.0 / particles.size();
cumsum[0] *= f;
cumsum[1] *= f;
cumsum[2] *= f;
return cumsum;
}
Much cleaner and more efficient solution using std::invoke:
std::array<double, 3> centroid(const std::vector<T> & particles)
{
if (particles.empty())
return {0, 0, 0};
std::array<double, 3> cumsum{0.0, 0.0, 0.0};
for (auto && p : particles)
{
cumsum[0] += std::invoke(&topology::Particle::getX, p);
cumsum[1] += std::invoke(&topology::Particle::getY, p);
cumsum[2] += std::invoke(&topology::Particle::getZ, p);
}
double f = 1.0 / particles.size();
cumsum[0] *= f;
cumsum[1] *= f;
cumsum[2] *= f;
return cumsum;
}
I am trying to find a way to count how many elements are equal in 2 different vectors of the same size in c++. The vectors hold structs and i want to compare the equality by a double variable of the struct shown on the example.
And to make it clear. I do NOT want to check if the 2 vectors are equal but only to count how many of their elements are.
The following doesn't work. It gives addresses instead of values. Also If I try to access the dist variable like pointsA[j].dist I get an error.
vector<struct PointWithDistance*> pointsA, pointsB;
//the struct
struct PointWithDistance {
Point *p;
double dist;
};
for (int j = 0; j < k; j++){
if (pointsA[j] == pointsB[j])
equalCount++;
}
vector<struct PointWithDistance*> pointsA, pointsB;
Did you mean to use pointers? If so, you have to do *(points[A]) (and b) because your current comparison compares the pointers, not their content.
Also, does the struct Point have an operator == so comparison on the type can be performed??
Do you want to force same positions? say, a vector {1,2,3} and a vector {2,3,4} by your algorithm will have 0 items equal, do you want that? If not, loop the first vector and use std::find (or std::upper_bound if the vector is sorted) on each element to the second vector.
Some quick code:
template <typename T=int> struct Point
{
T x,y;
bool operator==(const T& t) { return (x == t.x && y == t.y); }
};
std::vector<Point<>> p1 = {1,2,3};
std::vector<Point<>> p2 = {2,3,4};
for(auto& p : p1)
{
if (std::find(p2.begin(),p2.end(),p) != p2.end())
{
// similar++;
}
}
// or
assert(p1.size() == p2.size());
for(size_t i1 = 0 ; i1 < p1.size() ; i1++)
{
if (p1[i1] == p2[i1])
{
// equal++;
}
}
A generic solution to count the number of duplicates in 2 containers could look like this. Using std::transform_reduce to add (std::plus<>{}) the boolean result if an element was found in the container. Note how it can accep two different types of containers as long as their contained type stays the same (e.g. std::vector<int> and std::set<int>). The length of the containers don't have to be equal. There are two SFINAE implementations to differenciate between the case where T is a pointer and where it isn't:
#include <algorithm> //std::find, std::find_if
#include <cstddef> //std::size_t
#include <functional> //std::plus,
#include <iterator> //std::cbegin, std::cend
#include <numeric> //std::transform_reduce
#include <type_traits> //std::enable_if_t, std::is_pointer_v
namespace {
//core implementation for duplicate_count
template<class C, class F>
std::size_t duplicate_count_impl(const C& container, F pred) {
return std::transform_reduce(std::cbegin(container), std::cend(container), std::size_t{}, std::plus<>{}, pred);
}
}
//returns the number of duplicates in two (different) containers.
//overload for containers where T is a pointer type.
template<typename T, template <typename...> class C1, template <typename...> class C2, std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
std::size_t duplicate_count(const C1<T>& a, const C2<T> &b) {
return duplicate_count_impl(b, [&](T ptr_b) -> bool {
return std::find_if(std::cbegin(a), std::cend(a), [&](T ptr_a) -> bool {
return *ptr_a == *ptr_b;
}) != std::cend(a);
});
}
//returns the number of duplicates in two (different) containers.
//overload for containers where T is not a pointer type.
template<typename T, template <typename...> class C1, template <typename...> class C2, std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
std::size_t duplicate_count(const C1<T>& a, const C2<T> &b) {
return duplicate_count_impl(b, [&](T n) -> bool {
return std::find(std::cbegin(a), std::cend(a), n) != std::cend(a);
});
}
#include <iostream>
#include <vector>
#include <list>
//[duplicate_count implementations]
struct Point {
int a, b;
bool operator==(const Point& other) const {
return this->a == a && this->b == other.b;
}
};
int main() {
{
std::list<int> v = { 1, 2, 7, 7 };
std::list<int> u = { 0, 1, 2, 7 };
std::cout << "list<int>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
{
auto[a, b, c, d] = std::make_tuple(0, 1, 2, 3);
std::vector<int*> v = { &b, &c, &d, &d };
std::vector<int*> u = { &a, &b, &c, &d };
std::cout << "vector<int*>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
{
auto[a, b, c, d] = std::make_tuple(
Point{ 0, 0 },
Point{ 1, 1 },
Point{ 2, 2 },
Point{ 4, 4 });
std::vector<Point*> v = { &b, &c, &d, &d };
std::vector<Point*> u = { &a, &b, &c, &d };
std::cout << "vector<Point*>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
}
list<int> number of duplicates: 3
vector<int*> number of duplicates: 3
vector<Point*> number of duplicates: 3
Your shown solution is good, fast and efficient.
It has some minor problem that can easily be resolved. In your definition vector<struct PointWithDistance*> pointsA, pointsB;, variables pointsA and pointsB are vectors, containing pointer to structs.
With pointsA[n] you will get a pointer to the struct. But you want the struct by itself. So you simply need to dereference the gotten pointer. And since you want to access a member of a struct (usually done with variable.member), you can use (*(pointsA[j])).dist or pointsA[j]->dist.
If the size of your vectors are guaranteed the same, then you simply need to update your code to
vector<struct PointWithDistance*> pointsA, pointsB;
//the struct
struct PointWithDistance {
Point *p;
double dist;
};
for (int j = 0; j < k; j++){
if (pointsA[j]->dist == pointsB[j]->dist)
equalCount++;
}
That is the only thing you were missing.
You can use the std::inner_product algorithm:
#include <iostream>
#include <numeric>
#include <vector>
int main () {
const std::vector a{7, 7, 7, 7};
const std::vector b{7, 6, 7, 7};
const auto equalCount = std::inner_product(
a.begin(), a.end(), b.begin(), 0,
std::plus<>(), std::equal_to<>()
);
std::cout << equalCount << " of the elements are equal.\n";
}
outputs
3 of the elements are equal.
It is a generalization of the standard inner product,
using the functions + (plus) and == (equal_to),
instead of + and *.
Thus it computes
0 + (a[0] == b[0]) + (a[1] == b[1]) + ....
This uses the fact that false/true can be interpreted as 0/1.
I was wondering if there was a standard function that returns the minimum/maximum of the return values for a given range of elements. Something like this:
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it =
std::find_min_return_value(list.begin(), list.end(), std::abs, std::less<int>);
// it should be the iterator for -1
If there is no such, what is the best approach for a problem like this?
My list is long, I really don't want to copy it, and also don't want to call the function whose minimum return value I look for more than once per element. Thanks!
UPDATE:
Based on ForEveR's suggestion to use std::min_element, I made the following benchmarking tests:
std::vector<double> list = { -2, -1, 6, 8, 10 };
auto semi_expensive_test_function = [] (const double& a) { return asin(sin(a)); };
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin(), list.end(),
[&] (const double& a, const double& b) mutable
{
return(semi_expensive_test_function(a) < semi_expensive_test_function(b));
});
}
This worked just fine:
./a.out 11.52s user 0.00s system 99% cpu 11.521 total
After modifying the code to use a stateful lambda instead:
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin() + 1, list.end(),
[&, current_min_value = semi_expensive_test_function(*(list.begin()))] (const double& a, const double& b) mutable
{
double current_value = semi_expensive_test_function(b);
if(current_value < current_min_value)
{
current_min_value = std::move(current_value);
return true;
}
return false;
});
}
This resulted:
./a.out 6.34s user 0.00s system 99% cpu 6.337 total
Using stateful lambdas seems to be the way to go. The question is: is there a more code-compact way to achieve this?
With range-v3, it would be something like:
ranges::min(list, std::less<>{}, [](auto e) { return std::abs(e); });
Well, assuming Boost is like the standard library nowadays, you might use this:
#include <boost/range/adaptor/transformed.hpp>
#include <algorithm>
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto abs_list = list | boost::adaptors::transformed(+[](int v) { return std::abs(v); });
// ^ - read http://stackoverflow.com/questions/11872558/using-boost-adaptors-with-c11-lambdas
auto it = std::min_element(abs_list.begin(), abs_list.end(), std::less<int>{});
std::cout << *it;
}
If it'll get reused, and to give you another option, you could write your own generic algorithm following std conventions.
template <typename T, typename ForwardIt, typename UnaryFunction, typename Comparator>
ForwardIt find_min_return_value(ForwardIt first, ForwardIt last, UnaryFunction op, Comparator compare)
{
if (first == last)
return last;
ForwardIt smallestItr = first;
T smallestValue = op(*first);
for (auto itr = first + 1; itr != last; ++itr)
{
T current = op(*itr);
if (compare(current, smallestValue))
{
smallestValue = current;
smallestItr = itr;
}
}
return smallestItr;
}
Usage is then quite code-compact, and the operation will only be performed once per element:
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it1 = find_min_return_value<int>(list.begin(), list.end(), [](int i){ return std::abs(i); }, std::less<int>());
std::vector<std::string> strings = { "Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit" };
auto it3 = find_min_return_value<size_t>(strings.begin(), strings.end(), [](std::string s){ return s.length(); }, std::less<size_t>());
std::cout << *it1 << "\n"; // outputs -1
std::cout << *it3 << "\n"; // outputs sit
}
If you only suspect it may get reused one day it probably won't, and it's then overly complicated, and should then be just a simple function.
Coming from a Python world, I find the function std::iota very limited. Why is the interface restricted to not take any UnaryFunction ?
For instance I can convert
>>> x = range(0, 10)
into
std::vector<int> x(10);
std::iota(std::begin(x), std::end(x), 0);
But how would one do:
>>> x = range(0,20,2)
or even
>>> x = range(10,0,-1)
I know this is trivial to write one such function or use Boost, but I figured that C++ committee must have picked this design with care. So clearly I am missing something from C++11.
how about std::generate?
int n = -2;
std::generate(x.begin(), x.end(), [&n]{ return n+=2; });
int n = 10;
std::generate(x.begin(), x.end(), [&n]{ return n--;});
But how would one do:
x = range(0,20,2)
Alternatively to std::generate() (see other answer), you can provide your own unary function to std::iota(), it just have to be called operator++():
#include <iostream>
#include <functional>
#include <numeric>
#include <vector>
template<class T>
struct IotaWrapper
{
typedef T type;
typedef std::function<type(const type&)> IncrFunction;
type value;
IncrFunction incrFunction;
IotaWrapper() = delete;
IotaWrapper(const type& n, const IncrFunction& incrFunction) : value(n), incrFunction(incrFunction) {};
operator type() { return value; }
IotaWrapper& operator++() { value = incrFunction(value); return *this; }
};
int main()
{
IotaWrapper<int> n(0, [](const int& n){ return n+2; });
std::vector<int> v(10);
std::iota(v.begin(), v.end(), n);
for (auto i : v)
std::cout << i << ' ';
std::cout << std::endl;
}
Output: 0 2 4 6 8 10 12 14 16 18
Demo
Here is an idea of how one could implement Range():
struct Range
{
template<class Value, class Incr>
std::vector<Value> operator()(const Value& first, const Value& last, const Incr& increment)
{
IotaWrapper<Value> iota(first, [=](const int& n){ return n+increment; });
std::vector<Value> result((last - first) / increment);
std::iota(result.begin(), result.end(), iota);
return result;
}
};
Demo
With C++20 ranges, you can write it like this:
static auto stepped_iota(int start, int step) {
return std::ranges::views::iota(0) |
std::ranges::views::transform([=](int x) { return x * step + start; });
}
void f() {
for (int x : stepped_iota(0, 2)) { ... }
}
https://godbolt.org/z/3G49rs
Or, if you want the range to be finite:
static auto stepped_iota(int start, int end, int step) {
return std::ranges::views::iota(0, (end - start + step - 1) / step) |
std::ranges::views::transform([=](int x) { return x * step + start; });
}
Let's say I have a vector declared like this:
struct MYSTRUCT
{
float a;
float b;
};
std::vector<MYSTRUCT> v;
Now, I want to find all elements of v that share the same a, and average their b, i.e.
Say v contains these five elements {a, b}: {1, 1}, {1, 2}, {2, 1}, {1, 3}, {2, 2}
I want to get v[0], v[1], v[3] (where a is 1) and average b: (1 + 2 + 3)/3 = 2, and v[2] and v[4] (where a is 2) and average b: (1+2)/2 = 1.5
Afterwards v will look like this: {1, 2}, {1, 2}, {2, 1.5}, {1, 2}, {2, 1.5}
I'm not really familiar with STL or Boost so I can only figure out how to do this the "bruteforce" way in C++, but I'm guessing that the STL (for_each?) and Boost (lambda?) libraries can solve this more elegantly.
EDIT Just for reference, here's my (working) brute force way to do it:
for(int j = 0; j < tempV.size(); j++)
{
MYSTRUCT v = tempV.at(j);
int matchesFound = 0;
for(int k = 0; k < tempV.size(); k++)
{
if(k != j && v.a == tempV.at(k).a)
{
v.b += tempV.at(k).b;
matchesFound++;
}
}
if(matchesFound > 0)
{
v.b = v.b/matchesFound;
}
finalV.push_back(v);
}
Just thinking aloud, this may end up fairly silly:
struct Average {
Average() : total(0), count(0) {}
operator float() const { return total / count; }
Average &operator+=(float f) {
total += f;
++count;
}
float total;
int count;
};
struct Counter {
Counter (std::map<int, Average> &m) : averages(&m) {}
Counter operator+(const MYSTRUCT &s) {
(*averages)[s.a] += s.b;
return *this;
}
std::map<int, Average> *averages;
};
std::map<int, Average> averages;
std::accumulate(v.begin(), v.end(), Counter(averages));
BOOST_FOREACH(MYSTRUCT &s, v) {
s.b = averages[s.a];
}
Hmm. Not completely silly, but perhaps not compelling either...
Sketch of a solution:
sort(v.begin(), v.end());
vector<MYSTRUCT>::iterator b = v.begin(), e = v.end();
while (b != e) {
vector<MYSTRUCT>::iterator m = find_if(b, e, bind(&MYSTRUCT::a, _1) != b->a);
float x = accumulate(b, m, 0.f, _1 + bind(&MYSTRUCT::b,_2)) / (m-b);
for_each(b, m, bind(&MYSTRUCT::a, _1) = x);
b = m;
}
It's not a great one, though, since it's not exactly what was asked for (thanks to the sort), and still doesn't really feel clean to me. I think that some filter_iterators and transform_iterators or something could possibly give a much more functional-style answer.
Another approach, this one not in-place, though I think it's time-complexity-wise asymptotically the same.
typedef map<float, vector<float>> map_type;
map_type m;
BOOST_FOREACH(MYSTRUCT const &s, v) {
m[s.a].push_back(s.b);
}
BOOST_FOREACH(map_type::reference p, m) {
float x = accumulate(p.second.begin(), p.second.end(), 0.0f) / p.second.size();
p.second.assign(1, x);
}
BOOST_FOREACH(MYSTRUCT &s, v) {
s.b = m[s.a].front();
}
Again, though, it's just a slightly elegant way to code the brute-force solution, not a nice functional-style way.
Perhaps a brute force approach?...
struct MYAVG
{
int count;
float avg;
};
// first pass - calculate averages
for ( vector < MYSTRUCT >::iterator first = v.begin();
first != v.end(); ++first )
{
MYAVG myAvg;
myAvg.count = 1;
myAvg.avg = first->b;
if ( mapAvg.find( first->a ) == mapAvg.end() )
mapAvg[ first->a ] = myAvg;
else
{
mapAvg[ first->a ].count++;
mapAvg[ first->a ].avg =
( ( mapAvg[ first->a ].avg * ( mapAvg[ first->a ].count - 1 ) )
+ myAvg.avg ) / mapAvg[ first->a ].count;
}
}
// second pass - update average values
for ( vector < MYSTRUCT >::iterator second = v.begin();
second != v.end(); ++second )
second->b = mapAvg[ second->a ].avg;
I've tested this with the values you've supplied and get the required vector - It's not exactly optimal, but I think it's quite easy to follow (might be more preferable to a complex algorithm).
Avoid C-style! It's not what C++ is designed for. I'd like to emphasize clarity and readability.
#include <algorithm>
#include <iostream>
#include <map>
#include <numeric>
#include <vector>
#include <boost/assign/list_of.hpp>
using namespace std;
using namespace boost::assign;
struct mystruct
{
mystruct(float a, float b)
: a(a), b(b)
{ }
float a;
float b;
};
vector <mystruct> v =
list_of ( mystruct(1, 1) ) (1, 2) (2, 1) (1, 3) (2, 2);
ostream& operator<<(
ostream& out, mystruct const& data)
{
out << "{" << data.a << ", " << data.b << "}";
return out;
}
ostream& operator<<(
ostream& out, vector <mystruct> const& v)
{
copy(v.begin(), v.end(),
ostream_iterator <mystruct> (out, " "));
return out;
}
struct average_b
{
map <float, float> sum;
map <float, int> count;
float operator[] (float a) const
{
return sum.find(a)->second / count.find(a)->second;
}
};
average_b operator+ (
average_b const& average,
mystruct const& s)
{
average_b result( average );
result.sum[s.a] += s.b;
++result.count[s.a];
return result;
}
struct set_b_to_average
{
set_b_to_average(average_b const& average)
: average(average)
{ }
mystruct operator()(mystruct const& s) const
{
return mystruct(s.a, average[s.a]);
}
average_b const& average;
};
int main()
{
cout << "before:" << endl << v << endl << endl;
transform(v.begin(), v.end(),
v.begin(), set_b_to_average(
accumulate(v.begin(), v.end(), average_b())
));
cout << "after:" << endl << v << endl << endl;
}
You can use the "partition" algorithm along with "accumulate."
Example
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
struct test
{
float a;
float b;
test(const float one, const float two)
: a(one), b(two)
{
}
};
struct get_test_a {
float interesting;
get_test_a(const float i)
: interesting(i)
{
}
bool operator()(const test &value) const
{
static const float epi = 1e-6;
return value.a < interesting + epi &&
value.a > interesting - epi;
}
};
struct add_test_b {
float operator()(const float init, const test &value) const
{
return init + value.b;
}
};
int main(int argc, char **argv)
{
using std::partition;
using std::accumulate;
using std::distance;
typedef std::vector<test> container;
container myContainer;
// Say 'myVector' contains these five elements {a, b}:
// {1, 1}, {1, 2}, {2, 1}, {1, 3}, {2, 2}
myContainer.push_back(test(1, 1));
myContainer.push_back(test(1, 2));
myContainer.push_back(test(2, 1));
myContainer.push_back(test(1, 3));
myContainer.push_back(test(2, 2));
// I want to get v[0], v[1], v[3] (where a is 1) and
// average b: (1 + 2 + 3)/3 = 2,
// and v[2] and v[4] (where a is 2) and average b: (1+2)/2 = 1.5
const container::iterator split =
partition(myContainer.begin(), myContainer.end(),
get_test_a(1));
const float avg_of_one =
accumulate(myContainer.begin(), split, 0.0f, add_test_b())
/ distance(myContainer.begin(), split);
const float avg_of_others =
accumulate(split, myContainer.end(), 0.0f, add_test_b())
/ distance(split, myContainer.end());
std::cout << "The 'b' average of test values where a = 1 is "
<< avg_of_one << std::endl;
std::cout << "The 'b' average of the remaining test values is "
<< avg_of_others << std::endl;
return 0;
}
Documentation from the gcc headers
/**
* #brief Move elements for which a predicate is true to the beginning
* of a sequence.
* #ingroup mutating_algorithms
* #param first A forward iterator.
* #param last A forward iterator.
* #param pred A predicate functor.
* #return An iterator #p middle such that #p pred(i) is true for each
* iterator #p i in the range #p [first,middle) and false for each #p i
* in the range #p [middle,last).
*
* #p pred must not modify its operand. #p partition() does not preserve
* the relative ordering of elements in each group, use
* #p stable_partition() if this is needed.
*/
template<typename _ForwardIterator, typename _Predicate>
inline _ForwardIterator
partition(_ForwardIterator __first, _ForwardIterator __last,
_Predicate __pred)
/**
* #brief Accumulate values in a range with operation.
*
* Accumulates the values in the range [first,last) using the function
* object #a binary_op. The initial value is #a init. The values are
* processed in order.
*
* #param first Start of range.
* #param last End of range.
* #param init Starting value to add other values to.
* #param binary_op Function object to accumulate with.
* #return The final sum.
*/
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
_BinaryOperation __binary_op)
It seems the easiest way is to run a moderately complex functor over the colelction:
struct CountAllAverages {
typedef std::pair<float, unsigned> average_t;
std::map<float, average_t> averages;
void operator()(mystruct& ms) {
average_t& average = averages[ms.a];
average.second++;
average.first += ms.b;
}
float getAverage(float a) { return averages[a].first/averages[a].second; }
};
Writing C++, you should maintain balance between reusability (e.g. reuse existing algorithms and data structures) and readability. onebyone was close, but his solution can be further improved:
template<class T>
struct average {
T total;
int count;
mutable bool calculated;
mutable T average_value;
average & operator+=(T const & value) {
total += value;
++count;
calculated = false;
}
T value() const {
if(!calculated) {
calculated = true;
average_value = total / count;
}
return average_value;
}
};
std::map< float, average<float> > averages;
BOOST_FOREACH(MYSTRUCT &element, v) {
averages[element.a] += element.b;
}
BOOST_FOREACH(MYSTRUCT &element, v) {
element.b = averages[element.a].value();
}
Bonus points for having reusable "average" type.
struct MYSTRUCT {
float x;
float y;
operator float() const { return y; }
};
class cmp {
float val;
public:
cmp(float v) : val(v) {}
bool operator()(MYSTRUCT const &a) { return a.x != val; }
};
float masked_mean(std::vector<MYSTRUCT> const &in, MYSTRUCT const &mask) {
std::vector<float> temp;
std::remove_copy_if(in.begin(), in.end(), std::back_inserter(temp), cmp(mask.x));
return std::accumulate(temp.begin(), temp.end(), 0.0f) / temp.size();
}