Bug 5968 – std.algorithm.group by key function + groupFull
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-05-09T03:14:00Z
Last change time
2017-05-07T10:18:25Z
Keywords
bootcamp
Assigned to
nobody
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2011-05-09T03:14:50Z
Andrej Mitrovic has asked to split the following array in three arrays/ranges, according to the splitting predicate x<32:
[64, 64, 64, 32, 31, 16, 32, 33, 64]
A solution using group():
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];
}
}
Output:
[64, 64, 64, 32]
[31, 16]
[32, 33, 64]
Andrei has suggested the second item of the tuples that group() yields to be a lazy range instead just of counter (untested code). This is an improvement, and it makes group() closer to the Python itertools.groupby(). With this change the code becomes simpler:
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]); // g[1] is lazy
}
In Python groupby uses a key mapping function, like D schwartzSort(), that's more handy:
>>> 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]]
I suggest to change D group() to follow the Python groupby() design on this too. With this the D code improves further (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]);
}
Implementation note: unlike schwartzSort() there isn't a need to memorize the results of all the key mapping functions, this avoids slow memory allocations.
With tuple unpacking syntax sugar:
import std.stdio, std.algorithm;
void main() {
auto arr = [64, 64, 64, 32, 31, 16, 32, 33, 64];
foreach ((h, g); group!q{ a < 32 }(arr))
writeln(g);
}
Comment #1 by bearophile_hugs — 2013-02-12T17:42:44Z
I suggest to introduce a new function std.algorithm.groupFull that yields tuples where the second field of the tuple is a lazy range of all the grouped items, as in Python groupby.
This Python2 program returns all the longest words that have ordered chars:
from itertools import groupby
o = (w for w in map(str.strip, open("words.txt")) if sorted(w)==list(w))
print list(next(groupby(sorted(o, key=len, reverse=True), key=len))[1])
A similar program using groupFull:
import std.stdio, std.algorithm, std.range, std.file, std.string;
void main() {
"words.txt"
.readText()
.splitter()
.filter!isSorted()
.array()
.sort!q{a.length > b.length}()
.groupFull!q{a.length == b.length}()
.front[1]
.writeln();
}
Comment #2 by peter.alexander.au — 2013-09-22T07:17:04Z
*** Issue 11097 has been marked as a duplicate of this issue. ***