www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Remove array element within function

reply Rekel <paultjeadriaanse gmail.com> writes:
Is there an easy way to remove elements from an array passed in 
as a parameter?

Every example I find does something along the lines of:
```d
int[] a = ...
long index = countUntil(a, element);
a = a.remove(index);
````

But what do you do when you have?:
```d
void function(int[] a){
     . . .
     long index = countUntil(a, element);
     a.remove(index);
}
```

I assume I can't use a=a.remove(index), as I'm guessing this will 
only change the array inside the function itself, not the array 
that's actually used to call the function.

Am I the only one slightly unamused by how arrays/ranges work? 
They keep backfiring on me, or require weird additions other 
languages wouldn't require such as manually changing .length, or 
worrying about what operation returns a copy etc. (Kind of 
jealous of java's ease here)
Jul 05 2021
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Monday, 5 July 2021 at 13:10:55 UTC, Rekel wrote:

 ````

 But what do you do when you have?:
 ```d
 void function(int[] a){
     . . .
     long index = countUntil(a, element);
     a.remove(index);
 }
 ```
```d void function(ref int[] a) { ... } ``` An array is effectively a length/pointer pair that you are passing by value. If you want to do anything that affects either property, you need to pass by ref.
Jul 05 2021
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Monday, 5 July 2021 at 13:10:55 UTC, Rekel wrote:

 Am I the only one slightly unamused by how arrays/ranges work? 
 They keep backfiring on me, or require weird additions other 
 languages wouldn't require such as manually changing .length, 
 or worrying about what operation returns a copy etc. (Kind of 
 jealous of java's ease here)
D's arrays are friggin' awesome. I don't think they're any more difficult than in Java. In what situations do you need to manually change the length? Where do you worry about copies? It's possible there's something you're overlooking.
Jul 05 2021
parent reply Rekel <paultjeadriaanse gmail.com> writes:
Ah, ref, thanks, I didn't know if that would work as I was 
confused since arrays themselves are kind of already pointers.

On Monday, 5 July 2021 at 13:18:55 UTC, Mike Parker wrote:
 In what situations do you need to manually change the length? 
 Where do you worry about copies? It's possible there's 
 something you're overlooking.
I believe for setting length it was both when I wanted to initialize the array to a certain length (instead of having that inline with the declaration) & when I want to clear the array of all data. Though I also found threads in which people mentioned changing the length manually after calling remove. The copies were mostly at play in scenarios such as this one, but also when I'm appending lists to lists, in a `list.addAll(list2);` scenario. Though I guess I'm just not used to reassigning on the list variable after doing things with it. Also, is my use of long correct? The documentation wasn't very clear on the return type of remove, just calling it a 'number'. Again thanks for your help 😅
Jul 05 2021
next sibling parent reply Rekel <paultjeadriaanse gmail.com> writes:
I'm not sure if this is the place to talk about it, but on the 
same topic it's a little strange to me neither the Dlang Tour nor 
the arrays spec page mention removing elements. Even though 
basically everyone is going to use it sooner or later (most 
likely sooner).

Is that because it's part of the library? That's the only reason 
I could think of, while in a way I could make that argument for 
appending in the case it hadn't been given the `~` and `~=` 
operators.
Jul 05 2021
parent Mike Parker <aldacron gmail.com> writes:
On Monday, 5 July 2021 at 13:41:59 UTC, Rekel wrote:
 I'm not sure if this is the place to talk about it, but on the 
 same topic it's a little strange to me neither the Dlang Tour 
 nor the arrays spec page mention removing elements. Even though 
 basically everyone is going to use it sooner or later (most 
 likely sooner).

 Is that because it's part of the library? That's the only 
 reason I could think of, while in a way I could make that 
 argument for appending in the case it hadn't been given the `~` 
 and `~=` operators.
I had no involvement in creating the Tour, so I could only guess why it isn't covered. But if you think it should be, you can always submit an issue: https://github.com/dlang-tour/core/issues
Jul 05 2021
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Monday, 5 July 2021 at 13:34:50 UTC, Rekel wrote:
 Ah, ref, thanks, I didn't know if that would work as I was 
 confused since arrays themselves are kind of already pointers.
Except they're not :-) Think of them as struct instances with length and pointer fields. That's actually what they are.
 I believe for setting length it was both when I wanted to 
 initialize the array to a certain length (instead of having 
 that inline with the declaration)
```d int[] arr; arr = new int[](100); ```
 & when I want to clear the array of all data. Though I also 
 found threads in which people mentioned changing the length 
 manually after calling remove.
You shouldn't need to adjust the length after remove as long as you assign the return value to the original array. And actually, I didn't pay close enough attention to your original function. You didn't do that. It should be: ```d void removeItem(ref int[] a){ ... a = a.remove(index); } ``` Or this: ```d int[] removeElement(int[] a) { ... return a.remove(index); } // Later elems = removeElement(elems); ```
 The copies were mostly at play in scenarios such as this one, 
 but also when I'm appending lists to lists, in a 
 `list.addAll(list2);` scenario. Though I guess I'm just not 
 used to reassigning on the list variable after doing things 
 with it.
You never copy the contents of a dynamic array/slice. That only comes into play with static arrays: ```d void someFunc1(int[3] a) {} void someFunc2(int[] a); int[3] a = [1, 2, 3]; someFunc1(a); // This copies all the elements someFunc2(a); // This implicitly slices the array ``` Here are some resources that may help: https://ddili.org/ders/d.en/arrays.html https://ddili.org/ders/d.en/slices.html https://dlang.org/articles/d-array-article.html https://dlang.org/blog/2017/03/20/dont-fear-the-reaper/ Pay particular attention to the append operator + reserve. That plus the ability to slice without copying is where the real power of D's arrays lies. Then look into `std.array.Appender` when you have lots of items to append: https://dlang.org/phobos/std_array.html#Appender
 Also, is my use of long correct? The documentation wasn't very 
 clear on the return type of remove, just calling it a 'number'.
If you look in the function signature, it returns `ptrdiff_t`: https://dlang.org/phobos/std_algorithm_searching.html#countUntil Which is defined in `object.d`: https://github.com/dlang/druntime/blob/master/src/object.d#L61 It's `long` on 64-bit systems and `int` on 32-bit. So you can use `ptrdiff_t` or just `auto`: ```d auto index = countUntil(...); ```
 Again thanks for your help 😅
That's why we're here :-)
Jul 05 2021
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Monday, 5 July 2021 at 14:30:11 UTC, Mike Parker wrote:

 You never copy the contents of a dynamic array/slice. That only 
 comes into play with static arrays:
I should rephrase that. You aren't going to copy the contents of an array/slice just by passing it to a function.
Jul 05 2021
prev sibling parent Rekel <paultjeadriaanse gmail.com> writes:
Oh my, thank you both, that mostly cleared up my confusion, I had 
no clue this was struct related.
I'll be reading the references you gave me & probably submitting 
an issue 👍
Jul 05 2021
prev sibling parent reply jfondren <julian.fondren gmail.com> writes:
On Monday, 5 July 2021 at 13:10:55 UTC, Rekel wrote:
 Am I the only one slightly unamused by how arrays/ranges work? 
 They keep backfiring on me, or require weird additions other 
 languages wouldn't require such as manually changing .length, 
 or worrying about what operation returns a copy etc. (Kind of 
 jealous of java's ease here)
It's easy to start to learn about D's dynamic arrays and ranges and then welcome them as false friends. They're genuinely great, but they're not quite the same thing as what you recognized them as, and as long as this confusion persists, frustration will occur. You were probably expecting dynamic arrays to work like a class: ```d import std.array, std.algorithm; class Array { int[] data; this(int[] xs) { data = xs; } } void removeEvens(Array a) { a.data = a.data.filter!"a%2".array; } unittest { auto h = new Array([1, 2, 4, 5]); h.removeEvens; assert(h.data == [1, 5]); } ``` removeEvens gets a reference to a thing, it mutates it, job done. An int[] parameter though is a copy of a slice. It's more like like this struct: ```d import std.array, std.algorithm; struct Array { int* data; size_t length; this(int[] xs) { data = xs.ptr; length = xs.length; } } void removeEvens(Array a) { for (size_t i = 0; i < a.length; ++i) { if (a.data[i] % 2 == 0) { a.data[0 .. a.length].remove!(SwapStrategy.stable)(i); a.length--; i--; // ! } } } unittest { auto h = Array([1, 2, 4, 5]); h.removeEvens; assert(h.data[0 .. h.length] == [1, 5, 5, 5]); } ``` before removeEvens exits, its copy of a has become the intended 2-length array with only odd elements, but that copy is thrown away and the original 4-length array just sees its memory changed to have a 2-array prefix of its old odd elements, and then it has a suffix of garbage. Of ranges, they look like visual pipelines of functions that directly alter data that flows through them, but their actual result is *the pipeline* and not the data. Range functions take shorter bits of pipeline as an argument and produce longer bits of pipeline as a result, and their errors are all pipeline related: "I only connect to sorted ranges", "I only connect to randomly-accessible ranges", etc. On Monday, 5 July 2021 at 13:34:50 UTC, Rekel wrote:
 Also, is my use of long correct? The documentation wasn't very 
 clear on the return type of remove, just calling it a 'number'.
What use of long? remove returns the same type of range as it gets: ``` Range remove(SwapStrategy s = SwapStrategy.stable, Range, Offset...)(Range range, Offset offset) ^^^^^ ^^^^^ ^^^^^ ``` The first Range is the return type, the second Range is a template type parameter, and the third Range is the argument type. When you remove from an int[] you're calling a specialized function that returns an int[]
Jul 05 2021
parent reply Rekel <paultjeadriaanse gmail.com> writes:
On Monday, 5 July 2021 at 14:22:24 UTC, jfondren wrote:
 What use of long?

 remove returns the same type of range as it gets:
My apology, I meant to say countUntil instead of remove in that context.
Jul 05 2021
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 7/5/21 8:14 AM, Rekel wrote:

 On Monday, 5 July 2021 at 14:22:24 UTC, jfondren wrote:
 What use of long?

 remove returns the same type of range as it gets:
My apology, I meant to say countUntil instead of remove in that context.
countUntil says it returns ptrdiff_t, which is the signed version of size_t: https://dlang.org/spec/type.html#ptrdiff_t size_t is an alias of 'long' on 64-bit systems (it would be 'int' on 32-bit systems). So, you needen't guess the type of count; it is ptrdiff_t :) ptrdiff_t index = countUntil(a, element); However, its common to use 'auto' for return types of most algorithms as they may not be mentionable (although, not in this case; see Voldemort types): auto index = countUntil(a, element); And I always think of 'const' first to prevent silly mistakes: const index = countUntil(a, element); Some other people think of 'immutable' first in the same way: immutable index = countUntil(a, element); All should work in this case but 'const' and 'immutable' will have a "turtles all the way down" behavior which may be cumbersome in some case e.g. when the type has indirections. Ali
Jul 05 2021