Why is my function not deleting all numbers? - c

I made a function that will find any digits in an array and remove them. Here's my code:
int noNums (char *a) {
int i;
int deleteInd;
for (i = 0; (i < MAX_NAME_SZ) && (a[i] != '\0'); i++) {
if ((a[i] >= '0') && (a[i] <= '9')) {
deleteInd = i;
memmove (&a[deleteInd], &a[deleteInd + 1], strlen (a) - deleteInd);
}
}
}
If a number is by itself in the char array, then it is removed, no problem. However, if there are consecutive numbers in the array, then only every other digit will be deleted?
If my char array has
w12345678
then the array is changed to
w2468
instead of
w
Any ideas?

After you do the memmove(), the next element is now in the index of the element you just deleted. But your loop will do i++, so you won't check that index again. As a result, whenever there are two digits in a row, you skip the second one.
One way to fix this is to loop from the end of the array to the beginning, instead of from the beginning to the end.
Another way is to do i-- after doing the memmove(), to counteract the i++ that the loop will do.
if (isdigit(a[i]) {
deleteInd = i;
memmove (&a[deleteInd], &a[deleteInd + 1], strlen (a) - deleteInd);
i--;
}
BTW, you should use isdigit() to test whether a character is a digit.

Have you noticed that you are deleting the first digit then skipping one?
When iterating through the array, you start at position 0 and incrementing. When you delete a digit, you alter the string index.
i = 0 (char = w)
Index: 012345689
string: w12345678
i = 1 (char = 1)
Index: 012345689
string: w2345678
i = 2 (char = 3)
Index: 012345689
string: w2345678
Essentially, you are shifting the string over whenever you delete your character.
Don't increment i when you delete a character.
Note that deleteInd is not needed in your code, you could use i directly.

This is not an answer — Barmar's is — but both this and OP's other question shows they could use a fresh look into how to modify character arrays in place.
This is written in the hopes that this is useful to others learning C as well.
Elements, or sequences of elements, can be removed from an array efficiently, using a simple loop over its contents.
The key is to keep two indexes: one for the next element (or elements) to be examined, and one for the last element stored (or the next position to store to).
For example, to remove digits in an array, one can use the following pseudocode function:
Function removedigits(array, length):
Let i = 0 # Index of next element to examine, "input"
Let o = 0 # Position of next element to store, "output"
While (i < length):
If (array[i] is not a digit):
Let array[o] = array[i]
Let o = o + 1
End If
Let i = i + 1
End While
# For a string, we'll also want to terminate the array
# at o, because the rest of it contains garbage (old contents):
Let array[o] = '\0'
End Function
When dealing with sequences, it may be useful to keep multiple indexes. For example, to remove duplicate lines, one might use the following function:
Function removeduplicatelines(array):
Let i = 0 # Next position in the array to be examined
Let o = 0 # Next position in the array to store to
Let ostarted = 0 # Index at which the last line stored started at
# Loop over each input line:
While (array[i] != '\0'):
# Find the length of this line. It can end with a newline
# or at the end of the string. The newline is not included.
Let ilen = 0
While (array[i + ilen] != '\n' && array[i + ilen] != '\0'):
Let ilen = ilen + 1
End While
# If the already stored line is of different length
# (the +1 is for the newline, as it is not included in ilen)
# or if it does not match the input line, store input line.
If (ostarted + ilen + 1 != o || memcmp(array + ostarted, array + i, ilen) != 0):
# The lengths or content differs. Store the line.
# Copy ilen characters, starting at array[i],
# to array[o] onwards.
# Because the array parts do not overlap,
# we can safely use memcpy() here.
memcpy(array + o, array + i, ilen)
# It is now the last stored line.
Let ostarted = o
Let o = o + ilen
# If there is a newline following the line,
# store that too.
If (array[i + ilen] == '\n'):
Let array[o] = '\n'
Let o = o + 1
End If
Else:
# It is the same line again. Do not store.
End If
# Proceed to the next input line.
Let i = i + ilen
# Because the newline was not included in ilen,
# skip it if there is one.
If (array[i] == '\n'):
Let i = i + 1
End If
End While
# After index o, the array may contain old contents;
# so terminate the string at index o.
Let array[o] = '\0'
End Function
Note that memcmp() returns zero, if the ilen characters starting at array + ostarted match those starting at array + i.
This approach works, if we know o never exceeds i; that is, that we never overwrite array contents we haven't examined yet. But do note that o is allowed to be equal to i, as that just means we overwrite the same character we just examined, making no actual change in the array.
If we wanted to modify the function so that it skips empty lines, we add a new while loop before the existing one, to remove any leading newlines:
While (array[i] == '\n'):
Let i = i + 1
End While
and, to remove any empty lines, we modify the last part within the while loop into
# Because the newline was not included in ilen,
# skip it (and any additional newlines) if there is one.
While (array[i] == '\n'):
Let i = i + 1
End While
Finally, do note that the above removeduplicatelines() is very careful about not appending a newline after the last line, if there isn't one in the array to begin with.

Related

How to add element from the end to the start in empty array?

I ran into the theoretical problem, which doesn't let me solve my task.
I have an algorithms, that convert decimal to binary. It looks like this:
We have a decimal 65.
65%2 = 32(remainder 1), 32 % 2 = 16(remainder 0) etc. till we get 1. It's just "1".
So now I have something like this: "1000001" It's written from the last division with reminder to the first.
And I have to add it in the empty array. But I can't find the information how to add elements in array from the end to the start.
... to add elements in array from the end to the start.
In C, an array can not change size once it is declared. With int to string of binary text we know the maximally array size needed. For now, let us assume it is 12.
Set up an index to the end of the array.
In the %2 loop, decrement the index to work from end to start.
#define N 12
char buf[N];
int index = N-1;
buf [index] = '\0'; // Assign the last element of the array.
int negative = num < 0;
do {
int digit = num%2;
buf[--i] = abs(digit) + '0'; // Assign the previous array element.
num /= 2;
} while (num);
if (negative) {
buf[--i] = '-';
}
printf("<%s>\n", buf + i);
Notice the initial array elements were not used for small values of num.
Alternatives: We could use memmove() to shift the string to the first part of the array. Or loop though num twice, first to find the array size needed and the again, using a right-sized array, populate it. We could populate the array from the beginning (with the least significant digits first) and then reverse the string.
In the next version of C2x, we likely can simply do snprint(array, sizeof array, "%b", num);

How to delete an element from an array in C?

I've tried shifting elements backwards but it is not making the array completely empty.
for(i=pos;i<N-count;i++)
{
A[i]=A[i+1];
}
Actually, I've to test for a key value in an input array and if the key value is present in the array then I've to remove it from the array. The loop should be terminated when the array becomes empty. Here "count" represents the number of times before a key value was found and was removed. And, "pos" represents the position of the element to be removed. I think dynamic memory allocation may help but I've not learned it yet.
From your description and code, by "delete" you probably mean shift the values to remove the given element and shorten the list by reducing the total count.
In your example, pos and count would be/should be the similar (off by 1?) .
The limit for your for loop isn't N - count. It is N - 1
So, you want:
for (i = pos; i < (N - 1); i++) {
A[i] = A[i + 1];
}
N -= 1;
To do a general delete, given some criteria (a function/macro that matches on element(s) to delete, such as match_for_delete below), you can do the match and delete in a single pass on the array:
int isrc = 0;
int idst = 0;
for (; isrc < N; ++isrc) {
if (match_for_delete(A,isrc,...))
continue;
if (isrc > idst)
A[idst] = A[isrc];
++idst;
}
N = idst;

Mutating an array without extra space

I was given the following question in an interview, and couldn't find the solution.
Given is an array of chars length n, and "important section" (all chars in this section must be saved) length m where n >= m >= 0 as follows:
Without extra space, perform the following process:
Remove all occurrences of A and duplicate all occurrences of B, return a sub array of the mutated array. For example, for the above array [C,A,X,B,B,F,Q] n=7, m=5 ,output will be [C,X,B,B,B,B]. Note that the mutated array length is 6, since Q was in the redundant section and B was duplicated.
Return -1 if the operation can't be performed.
Examples:
n=2, m=2 , [A,B] => [B,B]
n=2, m=2 , [B,B] => -1 (since the result [B,B,B,B] is larger then the array)
n=3, m=2 , [A,B,C] => [B,B]
n=3, m=3 , [A,B,C] => [B,B,C]
n=3, m=2 , [Z,B,A] => [Z,B,B] (since A was in the redundant section)
Looking for a code example, Could this be done in O(n) time complexity?
Scan array to determine if is it possible to store mutated array in available space -- count As and B, and check N-M >= numB-numA
Walk array left to right: Shift elements to the left by the number of As so far (filling places of A)
Walk array right to left: Shift elements to the right by numB-B_so_far, inserting additional Bs
Start from the end of the input array. We will figure out from the back to the front what to fill in.
Look at the last significant character in the input (position m). If it is a, ignore it. Otherwise, add the symbol. Repeat until you read all the input.
This removes as. Now we will duplicate bs.
Start from the beginning of the array. Find the last value you wrote during the above steps. If it is a b, write two bs. If it is something else, just write one of them. Repeat. NOTE: if you ever "catch up", needing to write where you need to read, you don't have enough room and you output -1. Otherwise, return the part of the array from position 1 to the last read position.
Example:
Phase 1: removing A
CAXBBFQ
CAXBBFB
CAXBBBB
CAXBXBB
CAXCXBB
Phase 2: duplicating B
CAXCXBB
CXXCXBB
CXBBXBB
CXBBBBB
^^^^^^
Phase 1 is linear (we read m symbols and write no more than m).
Phase 2 is linear (we read fewer than m symbols and write no more than 2m).
m is less than n so everything is O(m) and O(n).
The code, with some optimizations, would look something like this, O(n):
// returns length of the relevant part of the mutated array or -1
public static int mutate(char[] a, int m) {
// delete As and count Bs in the relevant part
int bCount = 0, position = 0;
for (int i = 0; i < m; i++) {
if (a[i] != 'A') {
if (a[i] == 'B')
bCount++;
a[position++] = a[i];
}
}
// check if it is possible
int n = bCount + position;
if (n > a.length)
return -1;
// duplicate the Bs in the relevant part
for (int i = position - 1, index = n - 1; i >= 0; i--) {
if (a[i] != 'B') {
a[index--] = a[i];
} else {
a[index--] = 'B';
a[index--] = 'B';
}
}
return n;
}

How to fill an empty character array?

I'm trying decode an array of 1's and 0's using variable length coding. For example, if the string is [1 0 1 1], and A = [1 0] and B = [1 1], my program should give me a string something like: ['A', 'B'].
I first created an empty character array x = repmat(char(0),1,10)
But now when I detect a code word using a for loop and if statements, how do I add the character to this array x? Would it display the characters in the decoded string?
First of all, pre-defining the length of x is unnecessary in MATLAB because the language allows you resize arrays on-the-fly. That being said, preallocation is a sometimes a good idea because it will run faster.
Assuming you want to preallocate the length of x, you can assign a character to an element in x directly:
% Preallocate x
x = repmat(char(0),1,10);
% Assign a character to x
x(1) = 'A';
Where you can replace the 1 with any element in the array.
The challenge with this is that you need to keep track of where you are at in this preallocated array. If you already wrote characters to positions 1, 2, and 3, you need to know that the next assignment will write to the 4th element of x: x(4) = ....
A more elegant solution might be the following:
x = [];
if foundLetter
x(end) = 'A';
end
This adds the letter A to the end of the pre-defined character array x. It doesn't require that you preallocate the length of x.
You can index the character array x just as you would an array of doubles.
x(1) = 'A'; %Assign the char 'A' to the first element of x
.
.
.
x(10) = 'B'; %Assign the char 'B' to the tenth element of x
Here is a short example of what you would like to do.
clear decodedMsg
% define a dictionary between codes and corresponding characters
code{1,1} = 'A'; code{1,2} = '11';
code{2,1} = 'B'; code{2,2} = '101';
code{3,1} = 'C'; code{3,2} = '100';
% declare a sample message, corresponds to ABCBA
msg = [1 1 1 0 1 1 0 0 1 0 1 1 1];
%keeps track of the number of matches found, used to add to decodedMsg
counter = 1;
% get length of message and use to iterate through the msg
N = length(msg);
buffer = []; %must declare buffer if you are to use (end + 1) indexing later on
for i = 1:N
buffer(end + 1) = msg(i); %add the next msg value to the buffer
strBuf = strrep(num2str(buffer),' ',''); %convert buffer into string, e.x. [1 0 1] => '101'
findMatch = ismember(code(:,2),strBuf); %findMatch contains a 1 if a match is found
if any(findMatch) %if any element in findMatch is 1
decodedMsg(counter) = code{findMatch,1};%use that element to index a char in code cell array
counter = counter + 1; %increment counter since another code was found
buffer = []; %reset buffer for next string of 1s and 0s
end
end

C find string in text diagonally

Can anyone help me to organize such function ( as a homework I manage with crossword program ):
1) I have array of strings ( kind of text file ), where
row[0] = "str1"
row[1] = "str2" and etc.
2) The length of each string is same
3) Inputted word from stdin like abcd
I need to find word abcd in text located diagonally ( from left to right doesn't really matter just need kind of algorithm ) and replace found word with zeros.
First letter can be found with strchr but how correctly go forward and replace text with zeros? Can anyone give me any clue?
SOLUTION:
Tnx for minusing by the way. I've found another solution where each diagonal line is generated into 1 simple line, then I apply strstr to it to find position of match. Knowing position of match and length of matched string enables to detect precisely position of matched cells in array of lines.
I used such code ( maybe smn will find it useful ):
/* generating left-right diagonal string str */
for( col = cols - len; col >= 0; col --, i = 0 ){
/* where len is the length of searching string */
while( ( col+i ) < cols && i < rows ){
*(str + i) = res[i][col+i];
i ++;
}
*(str + i) = '\0';
//some match seq.
}
I did for left right diagonal from left top cell ( start of first string ) to last top cell ( end of first string ). Then same procedure for left column, so resulted algorithm will cover all strings.
You probably want to have a 2-dimentonal array of characters such as
char grid[i][j]
and if your first letter is at grid[i][j] than a next point in the diagonal is at
grid[i+1][j+1]
And so on for every combination of +1 and -1
Find first letter, lets say at row[5] the letter with index 7 (so row[5][7]).
Then check if row[6][8] is the second letter; or row[4][6], or row[4][8] or row[6][6]. Proceed with third letter, probably recursevily.

Resources