## digitalmars.D.learn - Filtering a tuple of containers with indices

• maik klein (46/46) Nov 17 2015 The question is also posted on
• anonymous (58/74) Nov 17 2015 [snip]
• maik klein (16/23) Nov 17 2015 Thanks but I have one question.
• anonymous (25/39) Nov 17 2015 Yes and no. It copies the Array structs, but it does not copy the
```The question is also posted on

https://stackoverflow.com/questions/33757981/filtering-a-tuple-of-containers-with-indicies

template tupIndexToRange(alias Tup, Indices...){
import std.meta;
static if(Indicies.length == 0){
alias tupIndexToRange = AliasSeq!();
}
else{
alias tupIndexToRange = AliasSeq!(Tup[ Indices[0] ][],
tupIndexToRange!(Tup,Indices[1..\$]));
}
}

void main{
alias Integrals = AliasSeq!(Array!int, Array!float,
Array!double);
Integrals integrals;

alias IntegralRange = tupIndexToRange!(integrals,0,1);
}

void main{
alias Integrals = AliasSeq!(Array!int, Array!float,
Array!double);
Integrals integrals;

alias IntegralRange = tupIndexToRange!(integrals,0,1);
}

I want to achieve something like this

auto range = zip(tupIndexToRange!(integrals,0,1));

I think the main problem is that Tup[ Indicies[0] ] doesn't work,
to me it should have expanded to this
AliasSeq!(itegrals[0][],integrals[1][]);

This is roughly what I want to achieve

alias Integrals = AliasSeq!(Array!int, Array!float,
Array!double);
Integrals integrals;
integrals[0].insertBack(1);
integrals[1].insertBack(2);
integrals[2].insertBack(3);

auto range = zip(tuple(integrals[0][],integrals[1][]).expand);
writeln(range);
foreach(e;range){
writeln("element: ",e);
}
But instead of "auto range =
zip(tuple(integrals[0][],integrals[1][]).expand);" I want it to
be generic "auto range = zip(tupIndexToRange!(integrals,
AliasSeq!(0, 1)).expand);"

Maybe I need use mixins?
```
Nov 17 2015
anonymous <anonymous example.com> writes:
```On 17.11.2015 15:32, maik klein wrote:
template tupIndexToRange(alias Tup, Indices...){

[snip]

I don't quite understand how that code is supposed to work. Maybe
there's just some detail missing, but it could also be that your
approach can't work.

This is roughly what I want to achieve

alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
Integrals integrals;
integrals[0].insertBack(1);
integrals[1].insertBack(2);
integrals[2].insertBack(3);

auto range = zip(tuple(integrals[0][],integrals[1][]).expand);
writeln(range);
foreach(e;range){
writeln("element: ",e);
}
But instead of "auto range =
zip(tuple(integrals[0][],integrals[1][]).expand);" I want it to be
generic "auto range = zip(tupIndexToRange!(integrals, AliasSeq!(0,
1)).expand);"

I think the problem can be split up into two independent tasks:

1) Select fields of a tuple by indices (to drop `integrals[3]`).
2) A "map" function for tuples (to apply `[]` to the selected arrays).

Here are two quick implementations of those applied to your problem:

----
template selectFromTuple(indices ...)
{
auto selectFromTuple(Types...)(Types values)
{
import std.typecons: tuple, Tuple;
static if (indices.length == 0) return Tuple!()();
else
{
auto tail = .selectFromTuple!(indices[1 .. \$])(values);
}
}
}

auto mapTuple(alias op, Types ...)(Types values)
{
import std.meta: staticMap;
import std.typecons: tuple;

alias ResultType(T) = typeof(op(T.init));
alias ResultTypes = staticMap!(ResultType, Types);

ResultTypes results;
foreach (i, v; values) results[i] = op(v);
return tuple(results);
}

void main()
{
import std.container.array;
import std.meta: AliasSeq;
import std.range: zip;
import std.stdio: writeln;

alias Integrals = AliasSeq!(Array!int, Array!float, Array!double);
Integrals integrals;
integrals[0].insertBack(1);
integrals[1].insertBack(2);
integrals[2].insertBack(3);

auto range = integrals
.selectFromTuple!(0, 1).expand
.mapTuple!(a => a[]).expand
.zip;

writeln(range);
foreach(e;range){
writeln("element: ",e);
}
}
----

That looks a lot like range based programming, which makes me think that
there could be a way to use actual range algorithms from std.algorithm
for this. But I don't see how.
```
Nov 17 2015
```On Tuesday, 17 November 2015 at 15:48:10 UTC, anonymous wrote:
On 17.11.2015 15:32, maik klein wrote:
[...]

[snip]

I don't quite understand how that code is supposed to work.
Maybe there's just some detail missing, but it could also be

[...]

Thanks but I have one question.

.selectFromTuple!(0, 1).expand

Does this result in a copy? I avoided doing it like this because
I was worried that I would copy every array. But I also don't
fully understand when D will copy.

Also doing

foreach(e;range){
e[0] = 10;
e[1] = 10.0f;
writeln("element: ",e);
}
foreach(e;range){
writeln("element: ",e);
}

doesn't mutate the range at all.
```
Nov 17 2015
anonymous <anonymous example.com> writes:
```On 17.11.2015 20:46, maik klein wrote:
.selectFromTuple!(0, 1).expand

Does this result in a copy? I avoided doing it like this because I was
worried that I would copy every array. But I also don't fully understand
when D will copy.

Yes and no. It copies the Array structs, but it does not copy the
elements of the arrays. If I remember correctly, std.container.Array
uses reference counting, and copying them should be cheap.

By the way, do you have a good reason to go with Array!int rather than
int[]? They're similar, but the builtin int[] may be easier to handle.

Also doing

foreach(e;range){
e[0] = 10;
e[1] = 10.0f;
writeln("element: ",e);
}
foreach(e;range){
writeln("element: ",e);
}

doesn't mutate the range at all.

You need to mark the `e` as `ref`: `foreach(ref e; range)`. Otherwise,
it's a copy of the element, and any changes to it are forgotten at the
end of the iteration.

But even with `ref` it doesn't work. Seems to be a bug in or a
limitation of `zip`. Works with `lockstep`:
----
auto ranges = integrals
.selectFromTuple!(0, 1).expand
.mapTuple!(a => a[]).expand;
auto range = ranges.zip;

import std.range: lockstep;
foreach(ref e0, ref e1; lockstep(ranges)){
e0 = 10;
e1 = 10.0f;
}
foreach(e;range){
writeln("element: ",e);
}
----
```
Nov 17 2015