10GeV
10GeV

Reputation: 475

Iterating over a column of a multidimensional array with std::count_if()?

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

Answers (2)

PaulMcKenzie
PaulMcKenzie

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

JaMiT
JaMiT

Reputation: 16997

Divide and conquer.

  • Use 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.

  • Use count_if
  • iterate over a single column of a multidimensional array
  • checking that the index of the row is within a certain range

Hmm... that first task looks doable. Maybe the last. Back to division.

  • Use std::count_if
  • iterate:
    • over an array
    • but it's multidimensional
    • only one column matters
  • checking that the index of the row is within a certain range

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

Related Questions