4
$\begingroup$

I have $L$ a 2d list, and $P$ a list of lists of $L$ 1d-index.
Lists in $P$ should be seen as "$L$-picking charts".
Let $(p_{i,1},p_{i,2},\dots,p_{i,n})$ be the $i$-th element of $P$, it's meaning will be:

  • pick a first object from the $p_{i,1}$-th list in $L$
  • pick a second object from the $p_{i,2}$-th list in $L$
  • ...
  • pick a last object from the $p_{i,n}$-th list in $L$

So for each element in $P$ I want to create all possible tuples of $L$ 2d-index accordingly to the criteria above.
($L$ objects are completely irrelevant, what matters is just their length... But of course, without a bit of context it would have seemed a weird problem)

L = {{1,2,3,4,5}, {13,17,19}, {81,-144,0,-6}, {0,1}, {1729}, {4,6,10,16,26}};

picker[P_] := 
  Module[{ranges, picked = {}},
    ranges = MapAt[Length@L[[#]]&, P, {All, All}];
    Do[
      picked = Join[picked, Transpose[{P[[i]], #}]&/@Tuples[ Range/@ranges[[i]] ]]
      , {i, Length@P}];
  picked
  ]

picker[{{3,4},{2,4,4,5}}]

{{{3, 1}, {4, 1}}, {{3, 1}, {4, 2}}, {{3, 2}, {4, 1}}, {{3, 2}, {4, 2}}, {{3, 3}, {4, 1}}, {{3, 3}, {4, 2}}, {{3, 4}, {4, 1}}, {{3, 4}, {4, 2}}, {{2, 1}, {4, 1}, {4, 1}, {5, 1}}, {{2, 1}, {4, 1}, {4, 2}, {5, 1}}, {{2, 1}, {4, 2}, {4, 1}, {5, 1}}, {{2, 1}, {4, 2}, {4, 2}, {5, 1}}, {{2, 2}, {4, 1}, {4, 1}, {5, 1}}, {{2, 2}, {4, 1}, {4, 2}, {5, 1}}, {{2, 2}, {4, 2}, {4, 1}, {5, 1}}, {{2, 2}, {4, 2}, {4, 2}, {5, 1}}, {{2, 3}, {4, 1}, {4, 1}, {5, 1}}, {{2, 3}, {4, 1}, {4, 2}, {5, 1}}, {{2, 3}, {4, 2}, {4, 1}, {5, 1}}, {{2, 3}, {4, 2}, {4, 2}, {5, 1}}}

The code works, but it seems a bit too sophisticated, is there a more elegant solution?
N.B.: The function should return all the tuples in one common list and not in Length@P separated lists

$\endgroup$

3 Answers 3

5
$\begingroup$

I could feel it in my bones that there was a compact way of doing this.

picker[pList_] := Tuples@MapIndexed[#2 &, L, {-1}][[#]] & /@ pList // Catenate
$\endgroup$
6
  • $\begingroup$ Amazing! Here's why I asked, you should definitely try Code Golf! Just a minor hassle: I'd like to have all the tuples in one list, where would you add Flatten? $\endgroup$ Jan 25 at 19:32
  • $\begingroup$ Sorry, my bad I didn't test your code... It returns the expected number of element, but are not in the requested form $\endgroup$ Jan 25 at 19:57
  • $\begingroup$ @DomenicoModica After postfixing Catenate, I believe the output is in the desired structure of a list of pairs of pairs as it matches your given output. $\endgroup$ Jan 25 at 20:14
  • $\begingroup$ No I mean that the return index themselves are wrong, they are not lists of pairs $\endgroup$ Jan 26 at 0:41
  • $\begingroup$ Ok, I've found the error, it should be MapIndexed[#2 &, L, {2}] not ...{-1} because L can contain any kind of more deeply nested object, here's why it didn't work with my actual list $\endgroup$ Jan 26 at 0:49
5
$\begingroup$

Here is a somewhat more MMA like implementation:

picker[d_] := Module[{t},
  Function[x, t = Tuples[Range /@ (Length[L[[#]]] & /@ x)]; 
    Transpose[{x, #}] & /@ t
    ] /@ d
  ]

picker[{{3, 4}, {2, 4, 4, 5}}]
$\endgroup$
7
  • $\begingroup$ A lot better, thanks, I was unaware on how to nest anonymous function $\endgroup$ Jan 25 at 18:27
  • $\begingroup$ I want all the tuple in one same list, should I add Flatten[#,1]&@(Function[...]/@d)? $\endgroup$ Jan 25 at 19:24
  • 1
    $\begingroup$ You could write: ... t = Transpose[{x, #}] & /@ t] /@ d ; Flatten[t, 1] ] $\endgroup$ Jan 25 at 19:40
  • $\begingroup$ It doesn't seem to work $\endgroup$ Jan 25 at 20:01
  • 1
    $\begingroup$ Do not worry. t is first used inside Function". However, when "Map" and "Function are done it is used to save the result. "Double use" ): $\endgroup$ Jan 26 at 9:00
1
$\begingroup$

Thanks to both the answers I've come up with the best mix of the two (I suppose...)

picker[P_] := Tuples[Function[x, {x, #} & /@ Range@Length@L[[x]]] /@ #] & /@ P // Catenate

Here's a snippet to test them

picker1[P_] := Function[x, Transpose[{x, #}] & /@ Tuples[Range /@ (Length[L[[#]]] & /@ x)]] /@ P
picker2[P_] := Tuples@MapIndexed[#2 &, L, {2}][[#]] & /@ P
picker3[P_] := Tuples[Function[x, {x, #} & /@ Range@Length@L[[x]]] /@ #] & /@ P

With[{Llen = 100, lmaxlen = 10, Plen = 10, pmaxlen = 7},
 L = Table[RandomInteger[100, RandomInteger[{1, lmaxlen}]], Llen];
 P = Table[RandomInteger[{1, Llen}, RandomInteger[{1, pmaxlen}]], Plen];
 ]

{Equal @@ (#[P] & /@ #), First@Timing@Do[#[P], {100}] & /@ #} &@{picker1, picker2, picker3}

Some results

{True, {11.9219, 0.921875, 0.59375}}  
{True, {5.328125, 0.546875, 0.265625}}
{True, {23.1875, 1.46875, 1.21875}}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.