A quick way to test whether all array elements are zero

Viewed 603

TL;DR I would like to know how to clean up the first if statement. I tried looking online and found nothing.

I am writing a program to test whether a number typed by the user has repeated digits. I have managed to create a 10-element boolean array (a[10]) such that if a[i] equals 0, this means that the digit 'i' is present in the typed number at most once. If a[i] equals 1, then the digit 'i' is present in the typed number at least twice (thus is repeated). Note 0<=i<=9.

Now I am trying to analyse the values in this array such that if all values equal zero then we type "Repeated digit". And if not we say which numbers are repeated.

if(a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 && a[5] == 0 && a[6] == 0 && a[7] == 0 && a[8] == 0 && a[9] == 0)  
       printf("No repeated digits");
  
else  
  printf("Repeated digits: "); 
  for(i = 0; i < 10; i++) {
        if(a[i] == 1)
        printf("%d ", i); 
    }   


I can't find a way of using a for loop combined with an if loop to clean up the first if statement. I have tried looking online but can't find a solution.

7 Answers

There is a trick to check if an array has all elements equal to N:

if (a[0]==N && memcmp(a, a+1, (array_length-1)*sizeof(a[0]) ) == 0)
    printf("All equal to N\n");

In you case you can do:

if (a[0]==0 && memcmp(a, a+1, 9*sizeof(a[0]) ) == 0)
    printf("All zeros\n");

This code is explicitly checking the first element to be zero, and then the memcmp is doing the following checks for you:

a[0] == a[1] && a[1] == a[2] &&....

This requires no extra allocated and initialized zero array as the other memcmp -based answers do.

Or use:

for (i=0; i<10; i++)
    if (a[i])
        break;

if (i==10) printf("No repeated digits");
else {
    //...
}

You can use a flag to indicate if an non-zero element is found.

int nonzero_found = 0;

for(i = 0; i < 10; i++) {
    if (a[i] != 0) {
        nonzero_found = 1;
        break;
    }
}

if (nonzero_found) {
    printf("Repeated digits: "); 
    for(i = 0; i < 10; i++) {
        if(a[i] == 1)
            printf("%d ", i); 
    }
} else {
    printf("No repeated digits");
}

Or if you really want to print Repeated digits: even if there are no repeated digits (like your original code):

int nonzero_found = 0;

for(i = 0; i < 10; i++) {
    if (a[i] != 0) {
        nonzero_found = 1;
        break;
    }
}

if (!nonzero_found) {
    printf("No repeated digits");
}

printf("Repeated digits: "); 
for(i = 0; i < 10; i++) {
    if(a[i] == 1)
        printf("%d ", i); 
}

You can use memcmp in combination with a compound literal of the same type set to all elements zero.

Assuming the elements of a are of type int:

if (memcmp(a, (int[10]){0}, sizeof a) == 0) {
  printf("all zero\n");
} else {
  printf("not all zero\n");
}

My suggestion for the loop:

char const* prefix = "No repeated digits";

for (i = 0; i < 10; ++i) {
    if (a[i] == 1 ) {
        // check if the prefix has been printed yet
        if (strcmp( prefix, "") != 0) {
            printf("Repeated digits: ");
            
            // and set the prefix to nothing so it won't be printed again
            prefix = "";
        }

        printf("%d ", i);
    }
}

printf( "%s\n", prefix);

The prefix variable gets used both as a flag of sorts and as the prefix for the line of text that gets printed.

One small benefit of this approach is that the array only gets walked once. Not a big deal (the "Big O" complexity isn't changed), but it's not nothing.

Note that this code assumes that the a[] elements are only 0 or 1 which was implied by the code in the original post but not explicitly stated.

You could make a function for the task. The logic is kind of the same as the flag approach, with the advantage that you can easily check and recheck different arrays in different places of your program.

The function would be something like

int allZeros( int *a, int len ) {

    int i = 0;
    
    while( i++ < len && !*a++ );
    
    return i == len+1;
}

And can be used in this way:

if( allZeros(a,10) ) puts("No repeated digits");

else {
    printf("Repeated digits: ");
    etc...
}

For starters you could write a function that counts how many times a digit is encountered in a number.

If the function accepts signed integer numbers then you need to process such numbers correctly.

Here is a demonstrative program.

#include <stdio.h>

#define N   10

struct Digits
{
    char a[N];
};

struct Digits split_to_digits( long long int n )
{
    const long long int Base = N;
    
    struct Digits digits = { 0 };
    
    do 
    {
        ++digits.a[n < 0 ? -( n % Base ) : n % Base];
    } while ( n /= Base );
    
    return digits;
}

int main(void) 
{
    while ( 1 )
    {
        printf( "Enter an integer number (0 - exit): " );
        
        long long int n;
        
        if ( scanf( "%lld", &n ) != 1 || n == 0 ) break;
        
        struct Digits digits = split_to_digits( n );
        
        int unique = 1;
        
        for ( size_t i = 0; unique && i < N; i++ )
        {
            unique = digits.a[i] < 2;
        }
        
        if ( unique )
        {
            puts( "No repeated digits." );
        }
        else
        {
            printf( "Repeated digits: " );
            
            for ( size_t i = 0; i < N; i++ )
            {
                if ( !( digits.a[i] < 2 ) )
                {
                    printf( "%zu ", i );
                }
            }
            
            putchar( '\n' );
        }
    }

    return 0;
}

Its output might look like

Enter an integer number (0 - exit): 12345
No repeated digits.
Enter an integer number (0 - exit): -12345
No repeated digits.
Enter an integer number (0 - exit): 12233445
Repeated digits: 2 3 4 
Enter an integer number (0 - exit): -12233445
Repeated digits: 2 3 4 
Enter an integer number (0 - exit): 0
Related