Print all elements in an array just once in C?

Viewed 1065

I have created an array in C and I know how to print every element in an array but couldn't figure it out how to not print repeated elements, or to be more precise, like I ask in the title, how can I print all elements just once?

For example my array is: [a b c d a a b d c c]

I want to print it like this: [a b c d]

I think that I should use for or while loop, but I don't know how. I have been thinking about this for hours and did some research but couldn't find anything valuable.

5 Answers

Here you are.

#include <stdio.h>

int main(void) 
{
    char a[] = { 'a', 'b', 'c', 'd', 'a', 'a', 'b', 'd', 'c', 'c' };
    const size_t N = sizeof( a ) / sizeof( *a );
    
    for ( size_t i = 0; i < N; i++ )
    {
        size_t j = 0;
        
        while ( j != i && a[j] != a[i] ) ++j;
        
        if ( j == i ) printf( "%c ", a[i] );
    }
    
    putchar ( '\n' );
    
    return 0;
}

The program output is

a b c d 

Or for example if you have a character array that contains a string then the same approach can be implemented the following way.

#include <stdio.h>

int main(void) 
{
    char s[] = { "abcdaabdcc" };

    for (const char *p = s; *p != '\0'; ++p )
    {
        const char *prev = s;
        
        while ( prev != p && *prev != *p ) ++prev;
        
        if ( prev == p ) printf( "%c ", *p );
    }
    
    putchar ( '\n' );
    
    return 0;
}

The program output is the same as shown above that is

a b c d 

As the array is an array of char containing lower case letters, there are pretty few different values. Consequently, you can make a table (aka another array) to track the already printed values.

Like:

#define MAX ('z' - 'a' + 1)  // Calculate the number of unique elements

int already_printed[MAX] = { 0 };       // Mark all chars as "not printed"
for (i = 0; i < SIZE_OFF_ARRAY; ++i)
{
    if (already_printed[array[i] - 'a'] == 0)   // If NOT printed yet
    {
        printf("%c\n", array[i]);               // Print it and
        already_printed[array[i] - 'a'] = 1;    // mark it as printed
    }
}

This gives you a simple O(N) solution. Having a O(N) solution is important for performance when handling large arrays.

Notice: This solution assumes that all array element are between 'a' and 'z' (both included) but can easilly be extended to support more a wider range.

A simple way:

#include <stdio.h>

int main() {

    int ascii[128] = { 0 };
    char input[] = "abcdaabdcc";
    
    for(int i = 0; input[i]; i++) {
        
        ++ascii[(int)input[i]];
    }
    
    for(int i = 0; i < 128; i++) {
        
        if( ascii[i] ) printf("%c ", i);
    }
    
    return 0;
}

The array ascii is used to keep track of the frequency of each of the 128 ascii characters with a non negative value (for example 'a' is 97 and '0' is 48). And then if the frequency of a character is not 0, you print the character.

I'm not sure what the type of the elements in the array is, but let's assume it's some type that C can "natively" compare. Then the conceptually simple solution is to sort the array, and the print it skipping duplicates. Sorting will ensure that the duplicates are adjacent. This approach will perform well in most circumstances.

First, let's set up some helper functions specific to the element type. You could remove the assign function if you only want to deal with char type, but it'll be inlined by the compiler anyway.

#include <stdlib.h>
#include <stdio.h>

// You can adapt the element type per your requirements
typedef char ElementType;

// This function assigns the source value to the destination:
// it does what *dst = *src would do.
static inline void assign(void *dst, const void *src)
{
    *(ElementType*)dst = *(const ElementType*)src;
}

// This is the "spaceship" comparison operator (<=> in C++) that
// merges less-than, equal, and more-than comparisons into one.
int compare(const void *l, const void *r) 
{
    const ElementType *L = l;
    const ElementType *R = r;
    if (*L < *R) return -1;
    if (*L > *R) return 1;
    return 0;
}

void print_element(const ElementType *el) { printf("%c", *el); }

Since we plan to sort the array, we need to copy it first - after all, a "printer" for an array shouldn't be modifying it. Such modifications are OK in tiny programs, but are just a bad habit, since if you look at the name of the function like print_unique, nothing hints you that it would modify the data it's supposed to print: that's not how printing normally acts. It'd be unexpected and very error prone.

The copy operation could be skipped if it's OK to modify the source array: its elements would need to be non-const then, and the print_unique function name would need to be changed to something like sort_and_print_unique.

ElementType *copy_array(const ElementType *src, const int count)
{
    ElementType *copy = malloc(sizeof(ElementType) * count);
    if (!copy) abort;
    for (int i = 0; i < count; ++i)
        assign(copy + i, src + i);
    return copy;
}

And now the unique element printer, and a test with the data you provided:

void print_unique(const ElementType *data, int const count)
{
    ElementType *copy = copy_array(data, count);

    qsort(copy, count, sizeof(ElementType), compare);

    printf("[");
    for (int i = 0; i < count; ++i) {
        if (i == 0 || compare(copy+i, copy+i-1) != 0) {
            if (i != 0) printf(" ");
            print_element(copy+i);
        }
    }
    printf("]\n");
}

int main() {
    const char array[] = "abcdaabdcc";
    print_unique(array, sizeof(array)/sizeof(*array) - 1);
}

Output: [a b c d]

The alternate, modifying implementation I mentioned above would be:

void sort_and_print_unique(ElementType *data, int const count)
{
    qsort(data, count, sizeof(ElementType), compare);

    printf("[");
    for (int i = 0; i < count; ++i) {
        if (i == 0 || compare(data+i, data+i-1) != 0) {
            if (i != 0) printf(" ");
            print_element(data+i);
        }
    }
    printf("]\n");
}

int main() {
    char array[] = "abcdaabdcc"; // note absence of const!
    sort_and_print_unique(array, sizeof(array)/sizeof(*array) - 1);
}

First, sort the array (use qsort(3) for example), then all the equal elements will be together. Then go in a one pass on the array saving the last element printed... if the one to be printed now is the same as the one printed last, just skip it and continue; to the next.

Related