0

What I am trying to achieve is to loop through an array ONCE and while doing so, also remove elements that my function returns false for. However the code I've written does not work. Any idea would be greatly appreciated because I am truly stuck and I don't understand what I am doing wrong

#include <stdio.h>

bool isitright(int i) {
    if (i/2 == 0) {
        return false;
    } else {
        return true;
    }
}

int main() {
    int arr[4] = {0, 1, 2, 3};
    int i = 0;
    while (i < 4) {
        if(!isitright(i)) {
            arr[i] = arr[i+1];
        }
        i++;
    }
}
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
Rrr Rrr
  • 5
  • 4
  • `arr[i]=arr[i+1];` It's worth noting that this doesn't change the length of the array. It's not really removing the element. It's really overwriting the current element with the next element. So for example if you have the list 0,1,2,3, and you remove 2, you end up with 0,1,3,3. – Nick ODell Nov 20 '21 at 05:57
  • is there any way to correct this? Is this even the right logic or i should try something completely different? – Rrr Rrr Nov 20 '21 at 06:04
  • Typically, you keep the length in a separate variable. So even though the actual array size doesn't change, the logical array size (i.e. your length variable) can be changed as needed. – user3386109 Nov 20 '21 at 06:09
  • 1
    Also note that after removing an element, every element that follows must be moved. And after two or more elements are removed, `arr[i]=arr[i+1]` won't work. You need two separate indexes to keep track of source and destination (`arr[k] = arr[i]`) and then you have to figure when to update those indexes. – user3386109 Nov 20 '21 at 06:18
  • 2
    You should end up with two indexes, both starting at 0. The first index is the next member of the input array to be tested; the second is the position of the next member of the output array. When the current element meets the criterion, copy it into the next available space in the array; when it doesn't skip it. At the end, the second index tells you how many elements are in the output array. You will sometimes copy an element over itself (before you delete any); it almost certainly isn't worth worrying about that if the data type is simple. It could matter with big structures to copy. – Jonathan Leffler Nov 20 '21 at 06:20
  • I [added an answer](https://stackoverflow.com/a/70043744/4561887) with 2 approaches demonstrated: 1) filtering the array into a new arrray, and 2) filtering the array in-place. Approach 1 has a time complexity of O(n), whereas Approach 2 could be up to O(n^2). So, whether the data structures in the array are big _or_ small, I'd go with 1 for sure. It's also sooo much easier to understand and maintain. – Gabriel Staples Nov 20 '21 at 07:47
  • _Style note:_ the pattern where you have a conditional that simply returns `true` or `false` is unnecessary. Also `isitright` is a terrible name for a function that checks to see if an `int` is odd. Try: `bool is_odd(int n) { return n % 2 == 1; }`. Additionally, your `while` loop could very readily be a `for` loop. `int i; for (i = 0; i < 4; i++) { if (!isitright(i)) { arr[i] = arr[i+1]; } }` – Chris Nov 20 '21 at 23:11

1 Answers1

3

(I still need to clean this answer up a bit more later--which I will when I make the time.)

How to "remove an element" from an array in C. You might also call this "filtering an array" in C.

You can't resize statically-allocated arrays in C. Instead, you just keep track of a size variable and adjust it accordingly and only print what you need to.

Approach 1: [excellent] modify original array in-place

[best approach if you are okay modifying the original array]: modify the array in place by picking up only the elements you want to keep, and copying them into the same array from the left to the right

For n elements in the array:
Time complexity: O(n)
Space complexity: O(1n) = O(n)

/// Get the number of elements in an array
#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))

int arr[] = {-3, -2, -1, 0, 1, 2, 3};
print_array(arr, ARRAY_LEN(arr));
size_t arr_len; // this will store the array's new length after modifications to it

// Remove values -1, 0, and +1 from the array
size_t i_write = 0;
for (size_t i_read = 0; i_read < ARRAY_LEN(arr); i_read++)
{
    if (is_it_right(arr[i_read]))
    {
        arr[i_write] = arr[i_read];
        i_write++;
    }
}
arr_len = i_write;

print_array(arr, arr_len);

Sample output:

[-3, -2, -1, 0, 1, 2, 3]
[-3, -2, 2, 3]

Approach 2: [excellent] filter an array into another array

[best approach if you want to leave the original array as-is]

For n elements in the array:
Time complexity: O(n)
Space complexity: O(2n) = O(n)

The easiest approach if you're trying to filter certain values from an array, however, is to just copy to a new array. So, here is a demo of that. In this demo, I begin with an array containing [-3, -2, -1, 0, 1, 2, 3], and I filter out all -1, 0, and +1 values with your little is_it_right() algorithm, to end up with [-3, -2, 2, 3] in the new array when I'm done.

To do that in-place in the same array would require a more-advanced algorithm that would almost certainly have the tradeoff of running slower, but, while not doubling the array space. The below approach will run very fast but I double the array space since I'm copying from one array into another rather than filtering the array in-place.

Anyway, study this carefully:

Run it online on OnlineGDB here.

Or download my full, runnable examples for both approaches below in my eRCaGuy_hello_world repo here: array_filter_and_remove_element.c

#include <stdbool.h>
#include <stdio.h>

/// Get the number of elements in an array
#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))

/// Return false for -1, 0, or +1, and return true otherwise.
bool is_it_right(int i)
{
    if (i/2 == 0)
    {
        return false;
    }
    
    return true;
}

void print_array(int arr[], size_t len)
{
    printf("[");
    for (size_t i = 0; i < len; i++)
    {
        printf("%i", arr[i]);
        if (i < len - 1)
        {
            printf(", ");
        }
    }
    printf("]\n");
}

int main()
{
    int arr[] = {-3, -2, -1, 0, 1, 2, 3};
    int arr_filtered[ARRAY_LEN(arr)];
    size_t arr_filtered_len; 
    
    // Remove values -1, 0, and +1 from the array
    int j = 0;
    for (size_t i = 0; i < ARRAY_LEN(arr); i++) 
    {
        if (is_it_right(arr[i]))
        {
            arr_filtered[j] = arr[i];
            j++;
        }
    }
    arr_filtered_len = j;
    
    print_array(arr, ARRAY_LEN(arr));
    print_array(arr_filtered, arr_filtered_len);
    
    return 0;
}

Output:

[-3, -2, -1, 0, 1, 2, 3]
[-3, -2, 2, 3]

Approach 3: [not good] "erase" an element by left-shifting

[way more complicated and processor-intensive and copy-intensive] filter an array in-place by left-shifting all the elements which lie to the right of an element you remove, each time you remove one element

For n elements in the array:
Time complexity: best-case (nothing to filter out): O(n); worst-case (all elements must be filtered out 1-at-a-time): O(n^2)
Space complexity: O(1n) = O(n)

This approach is way more complicated, more processor-intensive, and more copy-intensive. I recommend the approach above instead.

printf("\n====================================\n"
       "APPROACH 2: FILTER AN ARRAY IN-PLACE\n"
       "====================================\n");

// Remove values -1, 0, and +1 from the array **in place**
size_t arr_len = ARRAY_LEN(arr);
printf("Starting array:\n");
print_array(arr, arr_len);
size_t i = 0;
while (i < arr_len)
{
    if (is_it_right(arr[i]) == false)
    {
        // We don't want to keep this value at `arr[i]`, so overwrite it
        // by shifting all values from `i + 1` to the end of the array to
        // the left by 1 place.
        printf("Removing value `%i` from the array\n", arr[i]);
        size_t i_write = i;
        for (size_t i_read = i_write + 1; i_read < arr_len; i_read++)
        {
            arr[i_write] = arr[i_read];
            i_write++;
            print_array(arr, arr_len);
        }
        arr_len--;
        printf("One element has just been removed. Here is the new array:\n");
        print_array(arr, arr_len);
    }
    else // is_it_right(arr[i]) == true
    {
        // only increment `i` if we did NOT just left-shift a bunch of
        // elements by one index place
        i++;
    }
}
printf("Here is the final, filtered-in-place array:\n");
print_array(arr, arr_len);

Sample output:

====================================
APPROACH 2: FILTER AN ARRAY IN-PLACE
====================================
Starting array:
[-3, -2, -1, 0, 1, 2, 3]
Removing value `-1` from the array
[-3, -2, 0, 0, 1, 2, 3]
[-3, -2, 0, 1, 1, 2, 3]
[-3, -2, 0, 1, 2, 2, 3]
[-3, -2, 0, 1, 2, 3, 3]
One element has just been removed. Here is the new array:
[-3, -2, 0, 1, 2, 3]
Removing value `0` from the array
[-3, -2, 1, 1, 2, 3]
[-3, -2, 1, 2, 2, 3]
[-3, -2, 1, 2, 3, 3]
One element has just been removed. Here is the new array:
[-3, -2, 1, 2, 3]
Removing value `1` from the array
[-3, -2, 2, 2, 3]
[-3, -2, 2, 3, 3]
One element has just been removed. Here is the new array:
[-3, -2, 2, 3]
Here is the final, filtered-in-place array:
[-3, -2, 2, 3]

Again, the full, runnable examples for both approaches above are in my eRCaGuy_hello_world repo here: array_filter_and_remove_element.c

See also

  1. [a similar answer of mine in C++, but for strings--ex: std::string type] Removing all the vowels in a string in c++
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • 1
    Not my downvote, but a couple of reasons this might get a downvote: 1) There's a [debate in the community about how to handle homework questions](https://meta.stackoverflow.com/questions/334822), 2) There is a simple O(n) in-place solution to the problem. – user3386109 Nov 20 '21 at 19:33
  • I'll add the simple O(n) in-place solution next. I thought of it after writing the answer. As for the homework thing, I don't want to give people their homework on a silver platter either. It didn't occur to me at the time this was a homework question. It also makes a great interview question. – Gabriel Staples Nov 20 '21 at 20:37
  • @user3386109, This also isn't a great question, and lacks clarity. I should probably move this answer entirely to another question. I'm sure I can find a place for it, or just do my own Q&A for it if no great question for it exists. – Gabriel Staples Nov 20 '21 at 21:02
  • @user3386109, I still need to clean this answer up some more another day, and perhaps delete it and move it entirely to become an answer elsewhere, but I at least quickly added in the O(n) in-place solution to the beginning of my answer. I don't know why I hadn't seen that earlier, as it's basically the same exact thing as my "copy into a new array" solution, except I don't copy into a new array! :) I copy into the start of the same array. – Gabriel Staples Nov 21 '21 at 07:11
  • 1
    The in-place algorithm looks good, and like you said, it uses the same logic as the "copy" solution. Well done! – user3386109 Nov 21 '21 at 09:21