Vogel90
Vogel90

Reputation: 3

How to remove all struct with same value from an array

I'm working on my C programming skills, but i'm kinda stuck on a problem:

I have an array of Animal structs, which i can add animals to. One of the values is the animalId, however, there is no check for duplicate id's. So for example, I can add three dogs with the same id, but they can be on different positions within the array.

I'm trying to write a function which lets me remove alle the animals with the same id from the array. With the code below, the program only removes the first animal with the found id. Also when I try to remove the same id again, the program does not find the rest. For the return I want to return the total number of animals removed.

Can you guys give me some tips what I'm doing wrong?

EDIT: moved the line "*newNumberOfAnimalsPresent = numberOfAnimalsPresent - 1;" outside of the j loop.

int removeAnimal(int animalId, Animal *animalArray, size_t numberOfAnimalsPresent, size_t *newNumberOfAnimalsPresent)
{
    for (size_t i = 0; i < numberOfAnimalsPresent; ++i)
    {
        if (animalId == animalArray[i].Id)
        {
            for (size_t j = i; j < numberOfAnimalsPresent - 1; ++j)
            { 
                animalArray[j] = animalArray[j + 1];              
            }
            *newNumberOfAnimalsPresent = numberOfAnimalsPresent - 1; 
        }
    }
    return numberOfAnimalsPresent - *newNumberOfAnimalsPresent;
}

Upvotes: 0

Views: 78

Answers (3)

user3121023
user3121023

Reputation: 8286

Use two counters to iterate through the array. check is always incremented so every array element is checked. good is only incremented when there is no match so it reflects the array element that are good and will be kept.

#include <stdio.h>

typedef struct {
    int Id;
} Animal;

int removeAnimal(int animalId, Animal *animalArray, size_t numberOfAnimalsPresent, size_t *newNumberOfAnimalsPresent)
{
    size_t good = 0;
    size_t check = 0;
    for ( good = 0, check = 0; check < numberOfAnimalsPresent; ++check)
    {
        if (animalId != animalArray[check].Id)//not a match
        {
            if ( good != check) {//not equal
                animalArray[good] = animalArray[check];//make a copy
            }
            ++good;//increment
        }
    }
    *newNumberOfAnimalsPresent = good;
    return check - good;
}

int main ( void) {
    Animal pets[10] = {
        { 1}
        , { 2}
        , { 3}
        , { 4}
        , { 5}
        , { 2}
        , { 7}
        , { 8}
        , { 9}
        , { 2}
    };
    int duplicates = 0;
    size_t elements = 0;
    size_t show = 0;

    duplicates = removeAnimal ( 2, pets, 10, &elements);
    printf ( "2 found %d times\n", duplicates);

    while ( show < elements) {
        printf ( "pet[%zu] id is %d\n", show, pets[show].Id);
        ++show;
    }

    return 0;
}

Upvotes: 0

Maxime Vernier
Maxime Vernier

Reputation: 350

Since you will only iterate over every item if there is no removal, I think it is better to forget the for loop and use a while instead:

int removeAnimal(int animalId, Animal *animalArray, size_t numberOfAnimalsPresent, size_t *newNumberOfAnimalsPresent)
{
    *newNumberOfAnimalsPresent = numberOfAnimalsPresent;
    int i = 0;
    while (1)
    {
        if (*newNumberOfAnimalsPresent <= i)
        {
            break;
        }
        Animal animal = animalArray[i];
        if (animalId == animal.Id)
        {
            for (size_t j = i; j < *newNumberOfAnimalsPresent - 1; j++)
            {
                animalArray[j] = animalArray[j + 1];
            }
            (*newNumberOfAnimalsPresent)--;
        }
        else
        {
            i++;
        }
    }
    return numberOfAnimalsPresent - *newNumberOfAnimalsPresent;
}

Upvotes: 0

Maxime Vernier
Maxime Vernier

Reputation: 350

*newNumberOfAnimalsPresent = numberOfAnimalsPresent - 1; should go after the j loop no?

Upvotes: 2

Related Questions