remove

Remove all elements matching predicate.

@trusted
@"complexity", "O(length)"
size_t
remove
(
alias predicate
C
)
(
ref C c
)
if (
is(C == DynamicArray!(_),
_...
) &&
is(typeof(unaryFun!predicate(C.init[0])))
)

Return Value

Type: size_t

number of elements that were removed.

TODO: implement version that doesn't use a temporary array tmp, which is probably faster for small arrays.

Examples

construct and append from slices

alias T = int;
alias A = DynamicArray!(T, TestAllocator);
auto a = A([10,11,12].s);
a ~= a[];
assert(a[] == [10,11,12, 10,11,12].s);
a ~= false;
assert(a[] == [10,11,12, 10,11,12, 0].s);

construct and append using self

alias T = int;
alias A = DynamicArray!(T, TestAllocator);
auto a = A([10,11,12].s);
a ~= a;
assert(a[] == [10,11,12, 10,11,12].s);
alias T = int;
alias A = DynamicArray!(T, TestAllocator);
A a;
a.length = 1;
assert(a.length == 1);
assert(a.capacity == 1);

a[0] = 10;
a.insertBack(11, 12);
a ~= T.init;
a.insertBack([3].s);
assert(a[] == [10,11,12, 0, 3].s);

import std.algorithm.iteration : filter;

a.insertBack([42].s[].filter!(_ => _ is 42));
assert(a[] == [10,11,12, 0, 3, 42].s);

a.insertBack([42].s[].filter!(_ => _ !is 42));
assert(a[] == [10,11,12, 0, 3, 42].s);

a ~= a[];
assert(a[] == [10,11,12, 0, 3, 42,
			   10,11,12, 0, 3, 42].s);
alias T = int;
alias A = DynamicArray!(T);

A a;						// default construction allowed
assert(a.length == 0);
assert(a.length == 0);
assert(a.capacity == 0);
assert(a[] == []);

alias B = DynamicArray!(int, TestAllocator);
B b;
b.length = 3;
assert(b.length != 0);
assert(b.length == 3);
assert(b.capacity == 3);
b[0] = 1;
b[1] = 2;
b[2] = 3;
assert(b[] == [1, 2, 3].s);

b[] = [4, 5, 6].s;
assert(b[] == [4, 5, 6].s);

auto c = DynamicArray!(int, TestAllocator)();
c.reserve(3);
assert(c.length == 0);
assert(c.capacity >= 3);
assert(c[] == []);

version (none) // TODO: enable
static if (hasPreviewDIP1000)
	static assert(!__traits(compiles, { T[] f() @safe { A a; return a[]; } }));

const e = DynamicArray!(int, TestAllocator)([1, 2, 3, 4].s);
assert(e.length == 4);
assert(e[] == [1, 2, 3, 4].s);
alias T = int;
alias A = DynamicArray!(T, TestAllocator);

auto a = A([1, 2, 3].s);
A b = a.dupShallow;				// copy construction enabled

assert(a[] == b[]);		  // same content
assert(&a[0] !is &b[0]); // but not the same

assert(b[] == [1, 2, 3].s);
assert(b.length == 3);

b ~= 4;
assert(a != b);
a.clear();
assert(a != b);
b.clear();
assert(a == b);

const c = A([1, 2, 3].s);
assert(c.length == 3);

DIP-1000 return ref escape analysis

alias T = int;
alias A = DynamicArray!T;
version (none) // TODO: enable
static if (hasPreviewDIP1000)
	static assert(!__traits(compiles, { T[] leakSlice() pure nothrow @safe @nogc { A a; return a[]; } }));
T* leakPointer() pure nothrow @safe @nogc {
	A a;
	return a._store.ptr;	/+ TODO: shouldn't compile with -dip1000 +/
}
const _lp = leakPointer();	/+ TODO: shouldn't compile with -dip1000 +/

construct and insert from non-copyable element type passed by value

alias E = Uncopyable;
alias A = DynamicArray!(E);

A a = A(E(17));
assert(a[] == [E(17)]);

a.insertBack(E(18));
assert(a[] == [E(17),
			   E(18)]);

a ~= E(19);
assert(a[] == [E(17),
			   E(18),
			   E(19)]);

construct from slice of uncopyable type

alias _A = DynamicArray!(Uncopyable);
/+ TODO: can we safely support this?: A a = [Uncopyable(17)]; +/

construct with string as element type that needs GC-range

alias T = string;
alias A = DynamicArray!(T, TestAllocator);

A a;
a ~= `alpha`;
a ~= `beta`;
a ~= [`gamma`, `delta`].s;
assert(a[] == [`alpha`, `beta`, `gamma`, `delta`].s);

const b = [`epsilon`].s;

a.insertBack(b);
assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`].s);

a ~= b;
assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`, `epsilon`].s);

convert to string

alias T = int;
alias A = DynamicArray!(T);
DynamicArray!char sink;
A([1, 2, 3]).toString(sink.put);

iteration over mutable elements

alias T = int;
alias A = DynamicArray!(T, TestAllocator);
auto a = A([1, 2, 3].s);
foreach (const i, const e; a)
	assert(i + 1 == e);

iteration over constant elements

alias T = const(int);
alias A = DynamicArray!(T, TestAllocator);
auto a = A([1, 2, 3].s);
foreach (const i, const e; a)
	assert(i + 1 == e);

iteration over immutable elements

alias T = immutable(int);
alias A = DynamicArray!(T, TestAllocator);
auto a = A([1, 2, 3].s);
foreach (const i, const e; a)
	assert(i + 1 == e);

removal

alias T = int;
alias A = DynamicArray!(T, TestAllocator);

auto a = A([1, 2, 3].s);
assert(a == [1, 2, 3].s);

assert(a.takeFront() == 1);
assert(a == [2, 3].s);

a.popAt(1);
assert(a == [2].s);

a.popAt(0);
assert(a == []);

a.insertBack(11);
assert(a == [11].s);

assert(a.takeBack == 11);

a.insertBack(17);
assert(a == [17].s);
a.popBack();
assert(a.length == 0);

a.insertBack([11, 12, 13, 14, 15].s[]);
a.popAt(2);
assert(a == [11, 12, 14, 15].s);
a.popAt(0);
assert(a == [12, 14, 15].s);
a.popAt(2);

assert(a == [12, 14].s);

a ~= a;

removal

import nxt.container.traits : mustAddGCRange;

size_t mallocCount = 0;
size_t freeCount = 0;

struct S {
	pure nothrow @safe @nogc:
	alias E = int;
	import nxt.qcmeman : malloc, free;
	this(E x) @trusted {
		_ptr = cast(E*)malloc(E.sizeof);
		mallocCount += 1;
		*_ptr = x;
	}
	this(this) @disable;
	~this() nothrow @trusted @nogc {
		free(_ptr);
		freeCount += 1;
	}
	import nxt.gc_traits : NoGc;
	@NoGc E* _ptr;
}

/* D compilers cannot currently move stuff efficiently when using
   std.algorithm.mutation.move. A final dtor call to the cleared sourced is
   always done.
*/
const size_t extraDtor = 1;

alias A = DynamicArray!(S, TestAllocator);
static assert(!mustAddGCRange!A);
alias AA = DynamicArray!(A, TestAllocator);
static assert(!mustAddGCRange!AA);

assert(mallocCount == 0);

{
	A a;
	a.insertBack(S(11));
	assert(mallocCount == 1);
	assert(freeCount == extraDtor + 0);
}

assert(freeCount == extraDtor + 1);

// assert(a.front !is S(11));
// assert(a.back !is S(11));
// a.insertBack(S(12));

test OutputRange behaviour with std.format

import std.format : formattedWrite;
const x = "42";
alias A = DynamicArray!(char);
A a;
a.formattedWrite!("x : %s")(x);
assert(a == "x : 42");

check duplication via dupShallow

alias T = int;
alias A = DynamicArray!(T, TestAllocator);
static assert(!__traits(compiles, { A b = a; })); // copying disabled
auto a = A([10,11,12].s);
auto b = a.dupShallow;
assert(a == b);
assert(&a[0] !is &b[0]);

element type is a class

class T {
	this (int x) { this.x = x; }
	~this() nothrow @nogc { x = 42; }
	int x;
}
alias A = DynamicArray!(T, TestAllocator);
auto a = A([new T(10), new T(11), new T(12)].s);
assert(a.length == 3);
a.remove!(_ => _.x == 12);
assert(a.length == 2);

check filtered removal via remove

struct T { int value; }
alias A = DynamicArray!(T, TestAllocator);
static assert(!__traits(compiles, { A b = a; })); // copying disabled

auto a = A([T(10), T(11), T(12)].s);

assert(a.remove!"a.value == 13" == 0);
assert(a[] == [T(10), T(11), T(12)].s);

assert(a.remove!"a.value >= 12" == 1);
assert(a[] == [T(10), T(11)].s);

assert(a.remove!(_ => _.value == 10) == 1);
assert(a[] == [T(11)].s);

assert(a.remove!(_ => _.value == 11) == 1);
assert(a.length == 0);

construct from map range

import std.algorithm.iteration : map;
alias T = int;
alias A = DynamicArray!(T, TestAllocator);

A a = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => _^^2));
assert(a[] == [100, 400, 900].s);
a.popBackN(2);
assert(a.length == 1);
a.popBackN(1);
assert(a.length == 0);

A b = A([10, 20, 30].s[].map!(_ => _^^2));
assert(b[] == [100, 400, 900].s);
b.popBackN(2);
assert(b.length == 1);
b.popBackN(1);
assert(b.length == 0);

A c = A([10, 20, 30].s[]);
assert(c[] == [10, 20, 30].s);

construct from map range

alias T = int;
alias A = DynamicArray!(T, TestAllocator);

import std.typecons : RefCounted;
RefCounted!A x;

auto z = [1, 2, 3].s;
x ~= z[];

auto y = x;
assert(y[] == z);

const _ = x.toHash;

construct from static array

alias T = uint;
alias A = DynamicArray!(T, TestAllocator);

ushort[3] a = [1, 2, 3];

const x = A(a);
assert(x == a);
assert(x == a[]);

construct from static array slice

alias T = uint;
alias A = DynamicArray!(T, TestAllocator);
ushort[3] a = [1, 2, 3];
ushort[] b = a[];
const y = A(b); // cannot construct directly from `a[]` because its type is `ushort[3]`
assert(y == a);
assert(y == a[]);

GCAllocator

import std.experimental.allocator.gc_allocator : GCAllocator;
alias T = int;
alias A = DynamicArray!(T, GCAllocator);
const A a;
assert(a.length == 0);

construct with slices as element types

alias A = DynamicArray!(string);
const A a;
assert(a.length == 0);
alias B = DynamicArray!(char[]);
const B b;
assert(b.length == 0);

Meta