Bug 12592 – std.algorithm.keep to filter range elements and write back to the source range
Status
RESOLVED
Resolution
WORKSFORME
Severity
enhancement
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-04-18T11:03:54Z
Last change time
2018-03-31T13:45:06Z
Assigned to
No Owner
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2014-04-18T11:03:54Z
This code uses ranges in a UFCS chain to compute the unique items of immutable data items, and to return an array (if you don't need r to be an array you can omit the last 'array' call):
void main() {
import std.array: array;
import std.algorithm: sort, uniq, map;
immutable data = [10, 3, 2, 3, 4, 10];
const int[] r = data.dup.sort().uniq.array;
assert(r == [2, 3, 4, 10]);
}
But that allocates two arrays (beside the data), one in 'dup' and one in 'array'. To avoid that you can write:
void main() {
import std.algorithm: sort, uniq, copy;
immutable data = [10, 3, 2, 3, 4, 10];
auto int[] r = data.dup.sort().release;
r.length -= r.uniq.copy(r).length;
assert(r == [2, 3, 4, 10]);
}
But:
- This code is not very easy to write correctly the first time (I have had to compile and run it to be sure);
- Its semantics and purpose are not as immediately clear for the reader as the first version;
- Now r can't be const;
- We have lost the nice single UFCS chain, and the code is longer.
So a possible solution is to add a new function similar 'uniq' that performs that common operation (I don't know if release is needed here):
void main() {
import std.array: array;
import std.algorithm: sort, uniq, map;
immutable data = [10, 3, 2, 3, 4, 10];
const int[] r = data.dup.sort().release.keepUniq;
assert(r == [2, 3, 4, 10]);
}
But that seems to contain a common pattern that perhaps it's worth generalizing, a pattern that is equally usable here too:
const int[] r2 = data.dup.sort().filter!(x => x > 3).array;
That could become:
const int[] r2 = data.dup.sort().release.keepFilter!(x => x > 3);
Generalizing it could become:
const int[] r2 = data.dup.sort().release.keep!(filter!(x => x > 3));
And applied on the original code:
const int[] r = data.dup.sort().release.keep!uniq;
Another example:
auto data2 = [10, 3, 2, 3, 4, 10];
data2 = data2.remove!(x => x > 3);
assert(data2 == [3, 2, 3]);
Becomes:
data2.keep!(remove!(x => x > 3));
But this is not a good example because in my opinion it's much better to fix remove, see issue 10959 .
If you have more usage cases please add them below.
Comment #1 by nick — 2014-07-06T11:51:49Z
I've written a similar function to keep():
https://github.com/ntrel/stuff/blob/master/refill.d
My version isn't as easy to chain with UFCS as keep, but it does allow to refill a range from a different range altogether, rather than just a processed version of itself.
Comment #2 by nick — 2014-07-06T15:23:25Z
I just added an overload of refill so it can behave like your keep():
const r = data.dup.sort().release.refill!uniq;
Comment #3 by greensunny12 — 2018-02-10T20:18:25Z
We have group and filter, e.g.
---
import std.stdio;
void main() {
import std.algorithm;
immutable data = [10, 3, 2, 3, 4, 10];
assert(data.dup.sort.release.group.map!(a => a[0]).equal([2, 3, 4, 10]));
}
---
https://run.dlang.io/is/eBLeSa
Closing as WORKSFORME. Plese reopen if you feel that filter and group don't work for or you have more arguments or examples.
Comment #4 by nick — 2018-02-12T16:25:17Z
> assert(data.dup.sort.release.group.map!(a => a[0]).equal([2, 3, 4, 10]));
map does not produce an array, that's the idea. In fact using `group.map` is more complicated than just using `uniq`.