Finding the number of entries in a 2D array

Is there any way of finding the number of entries in the inner array of a 2 dimension array?

This is for a disco light project I'm working on: Computer controlled disco / theatre light system - Electronics information from PenguinTutor

I have an array containing the light sequences.
I have to define the size of the inner array otherwise it won't compile.

unsigned short lightPatterns[][maxNumSequence] = 
{
	{0},
	{17, 34, 68, 136},
	{1, 2, 4, 8, 16, 32, 64, 128},
	{136, 68, 34, 17},
	{128, 64, 32, 16, 8, 4, 2, 1},
	{102,153},
	{17, 34, 68, 136, 68, 34},
	{255}
};

I can find the number of entries in the first array using:

sizeof(lightPatterns)/sizeof(lightPatterns[0]);

but if I try the same on the inner array I get the value of maxNumSequence and not the number of entries.

There are of course ways around this, but they mean having to add additional characters to the array (eg. increasing the data type to int and using -1) or having another array to track the size of each sequence, but these make it more complex and means that another value needs to be tracked.

Is there anyway of finding out the number of entries defined in the inner array, or some other way of storing this so I can loop over them easily using a for loop?

Thanks
Stewart

PenguinTutor:
Is there any way of finding the number of entries in the inner array of a 2 dimension array?

Count the number of elements in the largest initializer list.

All of the "rows" of a 2-D array (matrix) have the same length (same number of "columns"). If any of the initializer lists is shorter than the number of columns, the remaining columns of that row will be initialized to zero. See Footnote.

const int maxNumSequence = 8; // Must be >= the length of the largest initializer list below

unsigned short lightPatterns[][maxNumSequence] = 
{
    {0},
    {17, 34, 68, 136},
    {1, 2, 4, 8, 16, 32, 64, 128}, // Longest row has eight columns
    {136, 68, 34, 17},
    {128, 64, 32, 16, 8, 4, 2, 1}, // Also has eight columns
    {102,153},
    {17, 34, 68, 136, 68, 34},
    {255},
    {9, 9, 9},
    {0}
};

void setup()
{
    Serial.begin(9600);
    int numRows = sizeof(lightPatterns)/sizeof(lightPatterns[0]);
    int numCols = sizeof(lightPatterns[0])/sizeof(lightPatterns[0][0]);
    Serial.print("Number of rows = ");Serial.println(numRows);
    Serial.print("Number of cols = ");Serial.println(numCols);
}
void loop(){}

Output:


Number of rows = 10
Number of cols = 8

Bottom line: You have to tell it the number of columns. The compiler can count the rows for you if you want it to.

Regards,

Dave

Footnote:

The following has the exact same initialization in the example code above. For stuff like this I actually prefer to list explicitly what the contents are, but it's a matter of style rather than substance. The results are the same.

unsigned short lightPatterns[10][8] = 
{
    {  0,   0,  0,   0,  0,  0,  0,   0},
    { 17,  34, 68, 136,  0,  0,  0,   0},
    {  1,   2,  4,   8, 16, 32, 64, 128},
    {136,  68, 34,  17,  0,  0,  0,   0},
    {128,  64, 32,  16,  8,  4,  2,   1},
    {102, 153,  0,   0,  0,  0,  0,   0},
    { 17,  34, 68, 136, 68, 34,  0,   0},
    {255,   0,  0,   0,  0,  0,  0,   0},
    {  9,   9,  9,   0,  0,  0,  0,   0},
    {  0,   0,  0,   0,  0,  0,  0,   0}
};

Thanks, but unfortunately that won't work. If I pad out with zeros then it turns all the lights off, which is not what I want for showing light sequences (eg. chaser will end up with a long pause).

I'd kind of guessed that there isn't a way to determine the length, but thought it would be worth a try. I've got so used to web programming using foreach etc., so a bit frustrating.

I didn't want to have an extra array to tell me the length of each sequence (I already have 3 arrays I need to keep in sync). So I think I'm going to have to add an array terminator (eg. -1), which means increasing the array to an int instead of a byte for the sake of 1 additional bit worth of information.

If I was programming for a PC then I'd have looked at creating a structure or object, but I've already gone beyond the memory of the ATMega168 based Diecimila. I've had to switch to a Duemilanove with a ATMega328 to use the additional RAM, but want to keep memory usage as low as possible as I still want to add new features.

If I pad out with zeros then it turns all the lights off, which is not what I want for showing light sequences (eg. chaser will end up with a long pause).

It's not a matter of you padding with extra 0s. The compiler is going to do that for you.

PenguinTutor:
Thanks, but unfortunately that won't work. If I pad out with zeros then it turns all the lights off...

Then don't declare a 2-D array. As I mentioned, with a 2-D array, all rows have the same number of columns. Period.

You can do it with pointers:

// Do these really have to be unsigned short?  Can't they be arrays of bytes?
// Anyhow...
// Declare a separate array for each pattern
//
const unsigned short lights0[] = {0};
const unsigned short lights1[] = {17, 34, 68, 136};
const unsigned short lights2[] = {1, 2, 4, 8, 16, 32, 64, 128};
const unsigned short lights3[] = {136, 68, 34, 17};
const unsigned short lights4[] = {128, 64, 32, 16, 8, 4, 2, 1};
const unsigned short lights5[] = {102,153};
const unsigned short lights6[] = {17, 34, 68, 136, 68, 34};
const unsigned short lights7[] = {255};

// An array of pointers to the patterns
// Each member of this array points to the first element of
// the corresponding pattern.
//
const unsigned short * lightPatterns[] = {
    lights0,
    lights1,
    lights2,
    lights3,
    lights4,
    lights5,
    lights6,
    lights7
};

//Make an array of sizes of the patterns so that you don't
// have to calculate them each time
const unsigned short sizes[] = {
    sizeof(lights0)/sizeof(lights0[0]),
    sizeof(lights1)/sizeof(lights1[0]),
    sizeof(lights2)/sizeof(lights2[0]),
    sizeof(lights3)/sizeof(lights3[0]),
    sizeof(lights4)/sizeof(lights4[0]),
    sizeof(lights5)/sizeof(lights5[0]),
    sizeof(lights6)/sizeof(lights6[0]),
    sizeof(lights7)/sizeof(lights7[0])
};
void setup()
{
    Serial.begin(9600);
}
void loop()
{
    int numPatterns = sizeof(lightPatterns)/sizeof(lightPatterns[0]);
    Serial.print("Number of patterns = ");Serial.println(numPatterns);
    
    for (int i = 0; i < numPatterns; i++) {
        Serial.print("Pattern ");Serial.print(i);Serial.print(":");
        for (int j = 0; j < sizes[i]; j++) {
            Serial.print("  ");Serial.print(lightPatterns[i][j]);
        }
        Serial.println();
    }
    delay(10000);
}

Output:


Number of patterns = 8
Pattern 0:  0
Pattern 1:  17  34  68  136
Pattern 2:  1  2  4  8  16  32  64  128
Pattern 3:  136  68  34  17
Pattern 4:  128  64  32  16  8  4  2  1
Pattern 5:  102  153
Pattern 6:  17  34  68  136  68  34
Pattern 7:  255

Regards,

Dave
[Edit: Added Footnote]
Footnote:
I'm not sure what you mean about keeping your arrays in "sync." Use a consistent method of defining them, and there is nothing to "sync."

If you don't want to worry about keeping separate arrays in "sync," you can put them in a struct;

struct LightPattern
{
    const byte * lights;
    const byte length;
};

// A separate array for each pattern
const byte lights0[] = {0};
const byte lights1[] = {17, 34, 68, 136};
const byte lights2[] = {1, 2, 4, 8, 16, 32, 64, 128};
const byte lights3[] = {136, 68, 34, 17};
const byte lights4[] = {128, 64, 32, 16, 8, 4, 2, 1};
const byte lights5[] = {102,153};
const byte lights6[] = {17, 34, 68, 136, 68, 34};
const byte lights7[] = {255};

//
// An array of structs, initialized with pointers to
//  the patterns and their respective lengths.
// 
//
const LightPattern lightPatterns[] = {
    {lights0, sizeof(lights0)/sizeof(lights0[0])},
    {lights1, sizeof(lights1)/sizeof(lights1[0])},
    {lights2, sizeof(lights2)/sizeof(lights2[0])},
    {lights3, sizeof(lights3)/sizeof(lights3[0])},
    {lights4, sizeof(lights4)/sizeof(lights4[0])},
    {lights5, sizeof(lights5)/sizeof(lights5[0])},
    {lights6, sizeof(lights6)/sizeof(lights6[0])},
    {lights7, sizeof(lights7)/sizeof(lights7[0])}
};
void setup()
{
    Serial.begin(9600);
}
void loop()
{
    int numPatterns = sizeof(lightPatterns)/sizeof(lightPatterns[0]);
    Serial.print("Number of patterns = ");Serial.println(numPatterns);
    
    for (int i = 0; i < numPatterns; i++) {
        Serial.print("Pattern ");Serial.print(i);Serial.print(":");
        for (int j = 0; j < lightPatterns[i].length; j++) {
            Serial.print("  ");Serial.print(lightPatterns[i].lights[j], DEC);
        }
        Serial.println();
    }
    delay(10000);
}

Same output as above.

Addressing individual elements of a pattern uses different notation, and some people might prefer the simpler [][] notation of the example in the main part of this post.

"Chacun à son goût!"
---davekw7x

So I think I'm going to have to add an array terminator (eg. -1),

What range of values do you actually need to store?
Couldn't you simply reserve, say, 255, and keep using bytes?

It's not a matter of you padding with extra 0s. The compiler is going to do that for you.

I appreciate that, but there is no way of knowing the difference between a zero that indicates all lights are off and a zero used to pad out the rest of the array (whether explicit or inserted by the compiler), so it turns off all the outputs.

// Do these really have to be unsigned short? Can't they be arrays of bytes?

Bytes are fine, but for some reason it fails to compile if I create a 2D array of bytes. But if it's a standard array it does. Don't understand that - but creating as unsigned short worked.

What range of values do you actually need to store?
Couldn't you simply reserve, say, 255, and keep using bytes?

Unfortunately no - it's an 8-way light output so by definition that needs a byte to output. Otherwise there are combinations that can't be shown.

The compiler manages to work out the size of storage for a 1D array if created with [], so I was hoping there was something that could be done with the 2D array. I guess that's because it's able to determine size of standard array to create at compile, whereas with a 2D the inner array has to be the same size across all elements.

I've done the following which works,

int lightPatterns[][maxNumSequence] = 
{
	{0, -1},
	{17, 34, 68, 136, -1},
	{1, 2, 4, 8, 16, 32, 64, 128, -1},
	{136, 68, 34, 17, -1},
	{128, 64, 32, 16, 8, 4, 2, 1, -1},
	{102,153, -1},
	{17, 34, 68, 136, 68, 34, -1},
	{255, -1}
};

with

if (lightPatterns[selectedPattern][i] == -1) {break;}

but I like Dave's suggestion of an Array of pointers. I wouldn't normally go with creating lots of individual variables as I normally program for a computer where I'd put the variables in a human configurable config file. But in the case of the ardiuno everything needs to be in code anyway (I have created a .h file for these settings but it's still writing in code), but in this case it could be an advantage in that I can give them sensible names - eg. calling the array chaser4, chaser8 etc... which would be easier to read than array1, array2 etc.

I guess the answer to my original question is no it's not possible, but thanks for the suggestions, as it's given me pointers to other ways to implement this. [no pun intended].