Reputation: 475
I have a multidimensional array of type double (double someArray[10][20]
). I'd like to:
a) use std::count_if()
to iterate over a single column of that array, returning the number of values greater than some number
b) also require that the row index of that number is within a certain range.
I know the basics of using std::count_if
(i.e. I know how to iterate over, say, some vector and return values greater than/less than/equal to some value, for example), but I'm not sure how to do this over a column of a multidimensional array, or how to check that the index of the element also satisfies some condition.
Upvotes: 0
Views: 496
Reputation: 35455
If you are willing to use boost::range, you can use the count_if
with a stride count.
The reason why this will work is that an array, regardless of the number of dimensions, will store its data in contiguous memory, thus random-access iteration will work exactly as it would a one-dimensional array.
Thus the goal is to figure out the starting column (easy), and instead of iterating one element at a time forward as you would with the "normal" std::count_if
, you want to iterate (in your case) 20
elements, since iterating that many will take you to the next row of the column you're interested in.
For a 2D array of M x N
size, the starting and ending addresses you would use for the STL algorithm functions would be:
start: &array2D[0][0]
end (one item passed the end): &array[M-1][N]
Given this information, here is an example using boost
:
#include <boost/range/adaptor/strided.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/assign.hpp>
#include <boost/range/algorithm.hpp>
#include <algorithm>
#include <iostream>
#include <numeric>
int main()
{
using namespace boost::adaptors;
using namespace boost::assign;
// declare and fill in the array with numbers starting from 0
double someArray[10][20];
std::iota(&someArray[0][0], &someArray[9][20], 0.0);
// let's get the address of the start of the third column
const double* startAddr = &someArray[0][2];
// here is the address of the end of the 2-dimensional array
const double* endAddr = &someArray[9][20]; // add up the third column
// create a SinglePass range consisting of the starting and ending address
// plus the stride count
auto str = std::make_pair(startAddr, endAddr) | strided(20);
// count how many items in the third column are less than 60
auto result = boost::range::count_if(str, [&](double val) { return val < 60; });
std::cout << result;
}
Output:
3
Upvotes: 1
Reputation: 16997
Divide and conquer.
count_if
to iterate over a single column of a multidimensional array, checking that the index of the row is within a certain range.Break the problem into smaller pieces, until they are small enough to solve.
count_if
Hmm... that first task looks doable. Maybe the last. Back to division.
std::count_if
Iterating over an array is not hard, given std::begin
and std::end
. The multidimensional aspect looks scary, so let's wait on that and see how far we can get. For now, just plug "iterate over an array" into "use count_if
".
std::count_if(std::begin(someArray), std::end(someArray), [](auto & element)
{ return ???; });
Hmm... the name element
is not accurate is it? When std::begin
is applied to double [][20]
, only one dimension is consumed. The result of de-referencing is not double
but double [20]
, so row
would be a more accurate name.
std::count_if(std::begin(someArray), std::end(someArray), [](auto & row)
{ return ???; });
The multidimensional aspect might have just taken care of itself. Can we focus on just one column? Let's assume a variable named column
is the desired column index, and target
can be the "some number" from the problem description.
std::count_if(std::begin(someArray), std::end(someArray), [column, target](auto & row)
{ return row[column] > target; });
So that leaves restricting the row index to a certain range. That is, iterate over the restricted range instead of over the entire array. Looks like a job for std::next
and std::prev
. We'll just need two more variables to give us the range; let's use from
and to
.
// Check only the desired rows
auto start = std::next(std::begin(someArray), from);
auto stop = std::prev(std::end(someArray), 9 - to);
return std::count_if(start, stop, [column, target](auto & row)
{ return row[column] > target; });
(I don't like that magic number 9
. Better would be ROWS-1
, assuming suitable constants have been declared so that the array's declaration can become double someArray[ROWS][COLS]
.)
Upvotes: 0