## digitalmars.D.learn - Is there a better way to write this split functionality?

• Andrej Mitrovic (26/26) May 08 2011 import std.stdio;
• bearophile (29/33) May 09 2011 Maybe this does what you want, but it's not very good:
Andrej Mitrovic <none none.none> writes:
```import std.stdio;
import std.array;
import std.range;
import std.algorithm;

void main()
{
auto arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];

auto newarr = arr[];
bool state = true;
while (arr.length)
{
newarr = state  ? array(until!("a < 32")(arr))
: array(until!("a >= 32")(arr));
arr = arr[newarr.length .. \$];
state ^= 1;

writeln(newarr);
}
}

The idea is to find as many elements in a sequence that conform to some
predicate, followed by as many elements that conform to another predicate. The
two predicates are switched on each run.

The above code will print:
[64, 64, 64, 32]
[31, 16]
[32, 33, 64]

Is there a better way to do this, some std.range/algorithm function I don't
know of?
```
May 08 2011
bearophile <bearophileHUGS lycos.com> writes:
```Andrej Mitrovic:

Is there a better way to do this, some std.range/algorithm function I don't
know of?

Maybe this does what you want, but it's not very good:

import std.stdio, std.algorithm;

void main() {
auto arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];

int last = 0;
foreach (g; group!q{ (a < 32) == (b < 32) }(arr)) {
writeln(arr[last .. last+g[1]]);
last += g[1];
}
}

With the change to group() Andrei talks about the code becomes a little better
(untested code):

import std.stdio, std.algorithm;

void main() {
auto arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];

foreach (g; group!q{ (a < 32) == (b < 32) }(arr))
writeln(g[1]);
}

In Python groupby uses a key mapping function, like D schwartzSort():

from itertools import groupby
arr = [64, 64, 64, 32, 31, 16, 32, 33, 64]
[list(g) for h,g in groupby(arr, key = lambda x: x < 32)]

[[64, 64, 64, 32], [31, 16], [32, 33, 64]]

If group uses a key mapping function as schwartzSort() the code improves
(untested):

import std.stdio, std.algorithm;

void main() {
auto arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];

foreach (g; group!q{ a < 32 }(arr))
writeln(g[1]);
}

Bye,
bearophile
```
May 09 2011
Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
```Those could be nice solutions, thanks.

My first code needlessly resizes the original array though. I could
simply track the lower index instead:

void main()
{
dchar[] arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];
dchar[] newarr;
size_t index;

bool state = true;
while (index < arr.length)
{
newarr = state ? array(until!("a < 32")(arr[index..\$]))
: array(until!("a >= 32")(arr[index..\$]));
index += newarr.length;
state ^= 1;

writeln(cast(int[])newarr);
}
}

You know what sucks? I can't assign a range with different predicates
to the same variable. E.g. this won't compile:
auto newarr = state ? (until!("a < 32")(arr[index..\$]))
: (until!("a >= 32")(arr[index..\$]));

Error: incompatible types for
((until(arr[index..__dollar],cast(OpenRight)1)) ?
(until(arr[index..__dollar],cast(OpenRight)1))):
'Until!(pred,dchar[],void)' and 'Until!(pred,dchar[],void)'

I mean they are basically the same range type, with only a different
predicate. Why every template instantiation has to be its own unique
type, I'll never understand.
```
May 09 2011