Gary Willoughby:
string[][] tags;
tags = tags.uniq!("a[0][1] == b[1][1]").array;
I think the right abstraction for your use case is:
auto tags = tags.flatten.uniq.array;
Where a std.range.flatten should take a range, range of ranges,
range of range of ranges, etc, and yield the items scanning them
by rows (flatten takes a template argument enumeration to change
the scanning pattern: by rows, by columns, bidirectional rows,
zig-zag, Hamming curve, Z curve, 2D rectangular blocks).
Probably flatten should have a compile-time argument to tell it
what a iterable range means. And an optional run-time argument
"maxDepth".
Ideally flatten should yield references of the items, so you can
increase all matrix items by 1 with code like:
int[][][] matrix = ...;
foreach (ref item; mat.flatten)
item++;
Bye,
bearophile