How to delete an element from an array in D - arrays

Concatenating an element x to an array items is easy in D, it's as if it were an array list:
arr ~= x;
but how do I remove an element at index i from items?
(Caveat: If I remove an element and then add a new element, the array must not be reallocated. So a simple slice won't work.)
Update:
Based on CyberShadow's answer about using assumeSafeAppend, I wrote this code:
static void removeAt(T)(ref T[] arr, size_t index)
{
foreach (i, ref item; arr[index .. $ - 1])
item = arr[i + 1];
arr = arr[0 .. $ - 1];
arr.assumeSafeAppend();
}
However, the problem happens when you have something like:
auto superArr = [0, 1, 2, 3, 4]; //Must not be modified
auto arr = superArr[0 .. $ - 1];
writeln(superArr);
arr.removeAt(0); //Should copy the slice and modify the copy
writeln(superArr); //but obviously doesn't
The base array of slice should not be modified if an element is removed from the slice; instead, the slice needs to be copied.
But I have no way of knowing if an array is a slice of a bigger array... so that doesn't work.
Any suggestions?

Copying my answer on digitalmars.D (thanks for forwarding):
As has been mentioned, std.algorithm.remove can be of help. You may want to look at three of its capabilities in particular: (a) remove multiple offsets in one pass, e.g. remove(a, 0, 4) removes the first and fifth element, (b) you can remove subranges, e.g. remove(a, tuple(1, 3)) removes the second through fourth element, and (c) if you don't care about the order in which elements are left after removal you may want to look into unstable remove, which does considerably less work.
Andrei

(Caveat: If I remove an element and then add a new element, the array must not be reallocated. So a simple slice won't work.)
The assumeSafeAppend function will tell the runtime not to reallocate the array when appending to it (i.e. it is an affirmation from the user that there aren't other slices which might be stomped by an append).
remove from std.algorithm does an in-place remove. If you're using std.container, there's also Array.linearRemove.

Well if order is of no importance you can copy the last element to the location of removal then reduce the array length by one.

If you just want to remove the first or last elements use slices:
array = array [1..$]
array = array [0..$-1]
Or a general way which works for a middle one as well:
array = array [0..unlucky] ~ array [unlucky+1..$]
If the elements aren't basic elements such as structs, floats, ints then arrays are implicitly arrays of pointers and this is an efficient operation.

There's no automated way of doing this, you'll have to shuffle the array items along, reset .length and then catenate.

Related

Is it safe to iterate an array while modifying it?

I know you shouldn't, I kind of know why. But I mean I don't understand my own code once I am trying really to think what's going on.
So I have an array with bunch of objects. I am iterating over it and once I find an object with specific type, I remove it from the array, and add another object into the array. So something like this:
var arr = parent.allchildren() //getting all the children in array
for ele in arr{
if(ele==somethingHere){
parent.remove(ele)
parent.add(new ele) //add new child into child array
}
}
If I have an array of 1,2,3,4,5, and I remove 3 and add a 6 while iterating, the actual array would be 1,2,4,5,6 but the array I am iterating would still be 1,2,3,4,5.
Which I think it would be fine, because at the end I still get what I want, which removed the element and added the element I need. However modifying the list while iterating it is bad and you shouldn't do that, but for my case I think it does what I need. What could be the potential issue in my case that I can't see?
One thing you may want to think about doing is making all of the changes at the end of the iteration. Instead of making the changes one by one, record the changes you want to make while iterating, and then actually make those changes once your loop is finished.
For example, you could make an array of elements to remove, and an array of elements to add.
//Our array where we record what we want to add
var elementsToAdd = [Any]()
//Our array of what elements we want to remove. We record the index at
//which we want to remove the element from the array
var indexesToRemoveAt = [Int]()
//Getting all the children in array
var arr = parent.allchildren()
//Enumerating an array allows us to access the index at which that
//element occurs. For example, the first element's index would be 0,
//the second element's index would be 1, the third would be 2, and so
//on
for (index,ele) in arr.enumerated() {
if(ele == somethingHere) {
indexesToRemoveAt.append(index)
elementsToAdd.append(newEle)
}
}
//Now that we have recorded the changes we want to make, we could make
//all of the changes at once
arr.remove(at: indexesToRemoveAt)
arr.append(contentsOf: elementsToAdd)
Note that removing array elements at multiple indexes would require the following extension to Array. If you wanted to avoid creating this extension, you could always just loop through the array of indexes and tell the array to remove at each individual index. All this extension function is really doing is looping through the indexes, and removing the array element at said index.
Array extension to remove elements at multiple indexes:
extension Array {
//Allows us to remove at multiple indexes instead of just one
mutating func remove(at indexes: [Int]) {
for index in indexes.sorted(by: >) {
if index <= count-1 {
remove(at: index)
}
}
}
}
I just tested in a playground with the following code:
var arr = ["hi", "bye", "guy", "fry", "sky"]
for a in arr {
if arr.count >= 3 {
arr.remove(at: 2)
}
print(a)
}
print(arr)
This prints:
hi
bye
guy
fry
sky
["hi", "bye"]
So it looks like when you use a for-in loop in Swift, the array is copied and changes you make to it will not affect the array you are iterating over. To answer your question, as long as you understand that this is the behavior, there's nothing wrong with doing this.

How to re-arrange elements of Array after Deleting

Recently I was reading a Programming book and found this question:
I have an array :
array = [2,3,6,7,8,9,33,22];
Now, Suppose I have deleted the element at 4th position i.e. 8 .
Now I have to rearrange the array as:
Newarray = [2,3,6,7,9,33,22];
How Can I do this. And I have to also minimize the complexity.
Edit I have no choice to make another copy of it.I have to only modify it.
You can "remove" a value from an array by simply copy over the element by the next elements, that's easy to do with memmove:
int array[8] = {2,3,6,7,8,9,33,22};
memmove(&array[4], &array[5], sizeof(int) * 3);
The resulting array will be {2,3,6,7,9,33,22,22}.
And from that you can see the big problem with attempting to "remove" an element from a compile-time array: You can't!
You can overwrite the element, but the size of the array is fixed at time of compilation and can't actually be changed at run-time.
One common solution is to keep track of the actual number of valid elements in the array manually, and make sure you update that size as you add or remove elements. Either that or set unused elements to a value that's not going to be used otherwise (for example if your array can only contain positive numbers, then you could set unused elements to -1 and check for that).
If you don't want to use a library function (why not?) then loop and set e.g.
array[4] = array[5];
array[5] = array[6];
and so on.
Do this, just use these two functions and it will work fine
index=4;//You wanted to delete it from the array.
memcpy(newarray,array,sizeof(array));
memmove(&newarray[index], &newarray[index + 1], sizeof(newarray)-1);
now the newarray contains your exact replica without the character that you wished to remove
You can simply displace each element from the delIdx(deletion index) one step forward.
for(int i=delIdx; i<(arr_size-1);i++)
{
arr[i]= arr[i+1];
}
If required you can either set the last element to a non-attainable value or decrease the size of the array.

Is there an idiomatic way to exchange two elements in a cell array?

I know that I can write it like this:
tmp = arr{i}
arr{i} = arr{j}
arr{j} = tmp
But is there a simpler way? For instance, in Python I'd write:
arr[i], arr[j] = arr[j], arr[i]
Standard, idiomatic way:
Use a vector of indices:
arr([i j]) = arr([j i]); %// arr can be any array type
This works whether arr is a cell array, a numerical array or a string (char array).
Not recommended (but possible):
If you want to use a syntax more similar to that in Python (with a list of elements instead of a vector of indices), you need the deal function. But the resulting statement is more complicated, and varies depending on whether arr is a cell array or a standard array. So it's not recommended (for exchanging two elements). I include it only for completeness:
[arr{i}, arr{j}] = deal(arr{j}, arr{i}); %// for a cell array
[arr(i), arr(j)] = deal(arr(j), arr(i)); %// for a numeric or char array
Not to confuse things, but let me another syntax:
[arr{[i,j]}] = arr{[j,i]};
or
[arr{i},arr{j}] = arr{[j,i]};
The idea here is to use comma-separated lists with curly-braces indexing.
Remember that when working with cell-arrays, ()-indexing gives you a sliced cell-array, while {}-indexing extracts elements from the cell-array and the return type is whatever was stored in the specified cell (when the index is non-scalar, MATLAB returns each cell content individually as a comma-separated list).

Worst-case time for adding 1 element to expanding array

Suppose we have an expanding array that doubles in size when we try to add something to it but it is filled.
According to the bottom of the slide "Amortization: Expanding Vectors" the worst-case time for adding one element is 2N, not N? Why is this?
I thought it would be N because copying N elements to the new, double sized array, would take N time.
For example, say I'm adding just one element to a filled array of size 4. It would go like this:
Make new array of size 8 (double of 4).
Copy all of elements of original array to new array (copy 4 times).
Set 5th element of the new array to the additional element (copy 1 time).
So that would copy elements 5 times, which is N + 1, not 2N?
The operations needed for adding an element to an exanding array is O(2N) because first you need to go through the whole array and check for empty space with O(N). Because this is the worst case there isn't any and you have to create a new array and copy the whole content from the first to the new array with O(N). Both operations combined result in O(2N).
When you think of array length the length is actually the size that is reserved for the array and not the number of elements inside. That is why you need to loop through the whole array for an empty space if you did not save the number of elements anywhere. I am speaking of 'empty space' because it is not stated that just elements were added and none were deleted inside the array.

Most efficient order to reduce like elements in multiple arrays so the array has 0 elements

Can someone please assist in how I would solve this problem; I need to figure out a way to eliminate like elements in multiple arrays in the best/quickest order in order to drive my array to 0 elements. I.E. if I had the following arrays:
'a {1,12,10,31}'
'b {12,21}'
'c {12,18,5,21}'
'd {12,18,21}'
I'd want to remove 12 -> 21 (b is done) then -> 18 (d is done)
This problem is really related to software incompatibilities... Any ideas would be helpful.
Thanks,
Pat
Well, it depends on how many is multiple arrays. If you only have two, you can sort them individually, and iterate over both at the same time in order, and remove.
However, this gets complicated quickly when you have an arbitrary number of arrays.
In this case, it is easiest to:
Put everything (merge) in a single array (named ARRAY)
Sort the array (ARRAY)
Iterate over the array (ARRAY), while removing elements occurring just once, and leaving a single copy of elements occurring multiple times
Then for each original array (eg. A, B, C, D), iterate over this original array (eg. A) along with ARRAY together, and remove elements in A that are also found in ARRAY.
For step 4., you probably want something like (written in pseudo-C code):
foreach (A = arrays [A, B, C, D]) { // for each original array
int j=0;
for (int i=0;i<A.size;i++) { // iterating over array A
// increase index j to iterate ARRAY (find closest # in ARRAY >= A[i])
while (j<ARRAY.size-1 && A[i]>ARRAY[j]) j++;
if (ARRAY[j]==A[i]) /* remove it */;
else /* keep it */;
}
}

Resources