remove

Eliminates elements at given offsets from range and returns the shortened range.

For example, here is how to remove a single element from an array:

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation; string[] a = [ "a", "b", "c", "d" ]; a = a.remove(1); // remove element at offset 1 assert(a == [ "a", "c", "d"]); ---- )

Note that remove does not change the length of the original range directly; instead, it returns the shortened range. If its return value is not assigned to the original range, the original range will retain its original length, though its contents will have changed:

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation; int[] a = [ 3, 5, 7, 8 ]; assert(remove(a, 1) == [ 3, 7, 8 ]); assert(a == [ 3, 7, 8, 8 ]); ---- )

The element at offset 1 has been removed and the rest of the elements have shifted up to fill its place, however, the original array remains of the same length. This is because all functions in std.algorithm only change content, not topology. The value 8 is repeated because move was invoked to rearrange elements, and on integers move simply copies the source to the destination. To replace a with the effect of the removal, simply assign the slice returned by remove to it, as shown in the first example.

$(LNAME2 remove-multiple, Removing multiple elements)

Multiple indices can be passed into remove. In that case, elements at the respective indices are all removed. The indices must be passed in increasing order, otherwise an exception occurs.

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation; int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove(a, 1, 3, 5) == [ 0, 2, 4, 6, 7, 8, 9, 10 ]); ---- )

Note that all indices refer to slots in the original array, not in the array as it is being progressively shortened.

Tuples of two integral offsets can be supplied to remove a range of indices:

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation, std.typecons; int[] a = [ 3, 4, 5, 6, 7]; // remove elements at indices 1 and 2 assert(remove(a, tuple(1, 3)) == [ 3, 6, 7 ]); ---- )

The tuple passes in a range closed to the left and open to the right (consistent with built-in slices), e.g. tuple(1, 3) means indices 1 and 2 but not 3.

Finally, any combination of integral offsets and tuples composed of two integral offsets can be passed in:

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation, std.typecons; int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; a = remove(a, 1, tuple(3, 5), 9); assert(a == [ 0, 2, 5, 6, 7, 8, 10 ]); ---- )

In this case, the slots at positions 1, 3, 4, and 9 are removed from the array.

$(LNAME2 remove-moving, Moving strategy)

If the need is to remove some elements in the range but the order of the remaining elements does not have to be preserved, you may want to pass SwapStrategy.unstable to remove.

$(RUNNABLE_EXAMPLE ---- import std.algorithm.mutation; int[] a = [ 0, 1, 2, 3 ]; assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]); ---- )

In the case above, the element at slot 1 is removed, but replaced with the last element of the range. Taking advantage of the relaxation of the stability requirement, remove moved elements from the end of the array over the slots to be removed. This way there is less data movement to be done which improves the execution time of the function.

remove works on bidirectional ranges that have assignable lvalue elements. The moving strategy is (listed from fastest to slowest):

  • If s == SwapStrategy.unstable && isRandomAccessRange!Range && hasLength!Range && hasLvalueElements!Range, then elements are moved from the end of the range into the slots to be filled. In this case, the absolute minimum of moves is performed.
  • Otherwise, if s == SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range && hasLvalueElements!Range, then elements are still moved from the end of the range, but time is spent on advancing between slots by repeated calls to range.popFront.
  • Otherwise, elements are moved incrementally towards the front of range; a given element is never moved several times, but more elements are moved than in the previous cases.
  1. Range remove(Range range, Offset offset)
    Range
    remove
    (
    Range
    Offset...
    )
    (
    Range range
    ,
    Offset offset
    )
    if (
    Offset.length >= 1 &&
    allSatisfy!(isValidIntegralTuple, Offset)
    )
  2. Range remove(Range range, Offset offset)
  3. Range remove(Range range)

Parameters

s

a SwapStrategy to determine if the original order needs to be preserved

range Range

a bidirectional range with a length member

offset Offset

which element(s) to remove

Return Value

Type: Range

A range containing elements of range with 1 or more elements removed.

Examples

import std.typecons : tuple;

auto a = [ 0, 1, 2, 3, 4, 5 ];
assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]);
a = [ 0, 1, 2, 3, 4, 5 ];
assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] );
a = [ 0, 1, 2, 3, 4, 5 ];
assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]);

a = [ 0, 1, 2, 3, 4, 5 ];
assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]);
a = [ 0, 1, 2, 3, 4, 5 ];
assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]);
import std.typecons : tuple;

// Delete an index
assert([4, 5, 6].remove(1) == [4, 6]);

// Delete multiple indices
assert([4, 5, 6, 7, 8].remove(1, 3) == [4, 6, 8]);

// Use an indices range
assert([4, 5, 6, 7, 8].remove(tuple(1, 3)) == [4, 7, 8]);

// Use an indices range and individual indices
assert([4, 5, 6, 7, 8].remove(0, tuple(1, 3), 4) == [7]);

SwapStrategy.unstable is faster, but doesn't guarantee the same order of the original array

assert([5, 6, 7, 8].remove!(SwapStrategy.stable)(1) == [5, 7, 8]);
assert([5, 6, 7, 8].remove!(SwapStrategy.unstable)(1) == [5, 8, 7]);

Meta