splitter

Lazily splits a range using an element or range as a separator. Separator ranges can be any narrow string type or sliceable range type.

Two adjacent separators are considered to surround an empty element in the split range. Use filter!(a => !a.empty) on the result to compress empty elements.

The predicate is passed to std.functional.binaryFun and accepts any callable function that can be executed via pred(element, s).

Notes: If splitting a string on whitespace and token compression is desired, consider using splitter without specifying a separator.

If no separator is passed, the predicate isTerminator decides whether to accept an element of r.

  1. auto splitter(Range r, Separator s)
  2. auto splitter(Range r, Separator s)
  3. auto splitter(Range r)
    splitter
    (
    alias isTerminator
    Range
    )
    (
    Range r
    )
    if (
    isForwardRange!Range &&
    is(typeof(unaryFun!isTerminator(r.front)))
    )
  4. auto splitter(Range s)

Parameters

r Range

The input range to be split. Must support slicing and .length or be a narrow string type.

isTerminator

The predicate for deciding where to split the range when no separator is passed

Return Value

Type: auto

An input range of the subranges of elements between separators. If r is a forward range or bidirectional range, the returned range will be likewise. When a range is used a separator, bidirectionality isn't possible.

If keepSeparators is equal to Yes.keepSeparators the output will also contain the separators.

If an empty range is given, the result is an empty range. If a range with one separator is given, the result is a range with two empty elements.

Examples

Basic splitting with characters and numbers.

import std.algorithm.comparison : equal;

assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ]));

int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
int[][] w = [ [1], [2, 3], [4, 5, 6] ];
assert(a.splitter(0).equal(w));

Basic splitting with characters and numbers and keeping sentinels.

import std.algorithm.comparison : equal;
import std.typecons : Yes;

assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "a", "|", "bc", "|", "def" ]));

int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];
assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));

Adjacent separators.

import std.algorithm.comparison : equal;

assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
assert("ab".splitter('|').equal([ "ab" ]));

assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ]));
assert("hello  world".splitter(' ').equal([ "hello", "", "world" ]));

auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
auto w = [ [1, 2], [], [3], [4, 5], [] ];
assert(a.splitter(0).equal(w));

Adjacent separators and keeping sentinels.

import std.algorithm.comparison : equal;
import std.typecons : Yes;

assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "", "|", "ab", "|", "" ]));
assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "ab" ]));

assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "a", "|", "b", "|", "", "|", "c" ]));
assert("hello  world".splitter!("a == b", Yes.keepSeparators)(' ')
    .equal([ "hello", " ", "", " ", "world" ]));

auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];
assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));

Empty and separator-only ranges.

import std.algorithm.comparison : equal;
import std.range : empty;

assert("".splitter('|').empty);
assert("|".splitter('|').equal([ "", "" ]));
assert("||".splitter('|').equal([ "", "", "" ]));

Empty and separator-only ranges and keeping sentinels.

import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.range : empty;

assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);
assert("|".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "", "|", "" ]));
assert("||".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "", "|", "", "|", "" ]));

Use a range for splitting

import std.algorithm.comparison : equal;

assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ]));
assert("a|b||c".splitter("||").equal([ "a|b", "c" ]));
assert("hello  world".splitter("  ").equal([ "hello", "world" ]));

int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
assert(a.splitter([0, 0]).equal(w));

a = [ 0, 0 ];
assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));

a = [ 0, 0, 1 ];
assert(a.splitter([0, 0]).equal([ [], [1] ]));

Use a range for splitting

import std.algorithm.comparison : equal;
import std.typecons : Yes;

assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>")
    .equal([ "a", "=>", "bc", "=>", "def" ]));
assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||")
    .equal([ "a|b", "||", "c" ]));
assert("hello  world".splitter!("a == b", Yes.keepSeparators)("  ")
    .equal([ "hello", "  ",  "world" ]));

int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));

a = [ 0, 0 ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
    .equal([ (int[]).init, [0, 0], (int[]).init ]));

a = [ 0, 0, 1 ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
    .equal([ [], [0, 0], [1] ]));

Custom predicate functions.

import std.algorithm.comparison : equal;
import std.ascii : toLower;

assert("abXcdxef".splitter!"a.toLower == b"('x').equal(
             [ "ab", "cd", "ef" ]));

auto w = [ [0], [1], [2] ];
assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));

Custom predicate functions.

import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.ascii : toLower;

assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x')
    .equal([ "ab", "X", "cd", "x", "ef" ]));

auto w = [ [0], [1], [2] ];
assert(w.splitter!("a.front == b", Yes.keepSeparators)(1)
    .equal([ [[0]], [[1]], [[2]] ]));

Use splitter without a separator

import std.algorithm.comparison : equal;
import std.range.primitives : front;

assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ]));
assert(equal(splitter!(a => a == ' ')("hello  world"), [ "hello", "", "world" ]));

int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
assert(equal(splitter!(a => a == 0)(a), w));

a = [ 0 ];
assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));

a = [ 0, 1 ];
assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));

w = [ [0], [1], [2] ];
assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));

Leading separators, trailing separators, or no separators.

import std.algorithm.comparison : equal;

assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
assert("ab".splitter('|').equal([ "ab" ]));

Leading separators, trailing separators, or no separators.

import std.algorithm.comparison : equal;
import std.typecons : Yes;

assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "", "|", "ab", "|", "" ]));
assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
    .equal([ "ab" ]));

Splitter returns bidirectional ranges if the delimiter is a single element

import std.algorithm.comparison : equal;
import std.range : retro;
assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ]));

Splitter returns bidirectional ranges if the delimiter is a single element

import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.range : retro;
assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
    .retro.equal([ "def", "|", "bc", "|", "a" ]));

Splitting by word lazily

import std.ascii : isWhite;
import std.algorithm.comparison : equal;
import std.algorithm.iteration : splitter;

string str = "Hello World!";
assert(str.splitter!(isWhite).equal(["Hello", "World!"]));

See Also

std.regex._splitter for a version that splits using a regular expression defined separator, std.array._split for a version that splits eagerly and splitWhen, which compares adjacent elements instead of element against separator.

Meta