7
$\begingroup$

I have a very large ragged list result that is a list of list of pairs where sometimes the sub-list contains the empty list.

res = {{{20, 1}, {200, 2}}, {{}}, {{175, 1}}};

In the above minimal example there are 3 list with 2 containing list of pairs and one the empty list. The actual list is much longer with longer sub-list.

I need to place a value from RandomVariate as the third entry in each pair. However, to keep performance reasonable, I only want to call RandomVariate once for the total number of values I need. In this case I would call it for 3 values.

If the 3 values returned are

vals = {a, b, c};

Then is it possible to efficiently produce

{{{20, 1, a}, {200, 2, b}}, {{}}, {{175, 1, c}}}

without loops/procedural programming? For context the actual list will hold around 100,000+ sub-list each with zero to five pairs.

$\endgroup$
9
$\begingroup$

Perhaps

ClearAll[f]
f[x_, vals_] := Module[{n = 0}, Replace[x, y_ /; y != {} :> Append[y, vals[[++n]]], {-2}]]

Example:

res = {{{20, 1}, {200, 2}}, {{}}, {{175, 1}}};
vals = {a, b, c};

f[res, vals]

{{{20, 1, a}, {200, 2, b}}, {{}}, {{175, 1, c}}}

$\endgroup$
  • $\begingroup$ Block with PreIncrement and that negative Replace level spec. You are too clever! (1+) while we wait. $\endgroup$ – Edmund Oct 12 '17 at 3:34
  • $\begingroup$ @Edmund, thank you for the vote. I am sure I learned the trick somewhere on this site:) $\endgroup$ – kglr Oct 12 '17 at 3:47
  • $\begingroup$ I was "chastised" once by @Mr.Wizard about using Block instead of Module in this construction, because there's some weird unexpected "psuedo-leakiness" in Block. I can't remember exactly what the issue is, though. $\endgroup$ – march Oct 12 '17 at 3:48
  • $\begingroup$ @march, being "chastised"... it does ring a bell:) $\endgroup$ – kglr Oct 12 '17 at 3:49
  • $\begingroup$ @march Evaluating f[res, {n, b, c}] reveals the problem. Module will fix it. Still +1, though. $\endgroup$ – WReach Oct 12 '17 at 4:05
2
$\begingroup$
With[{pos = Position[res, {_Integer, _Integer}]}, 
 ReplacePart[res, 
  Thread[Rule[pos, MapThread[Append, {Extract[res, pos], vals}]]]]]
$\endgroup$
2
$\begingroup$
Map[Replace[{x_, y_} :> {x, y, RandomReal[]}], res, {2}]

Edit (to make all rvs at once):

res = {{{20, 1}, {200, 2}}, {{}}, {{175, 1}}};
n = 2*Length[res]
rvs = RandomReal[1, n]
i = 1
Map[Replace[{x_, y_} :> {x, y, rvs[[i++]]}], res, {2}]
$\endgroup$
  • $\begingroup$ Thanks, but I only want to call RandomVariate once. $\endgroup$ – Edmund Oct 12 '17 at 3:28
  • $\begingroup$ @Edmund Ah, I misunderstood what you meant by that. How will you know how many values you need? $\endgroup$ – Alan Oct 12 '17 at 16:24
  • $\begingroup$ By the number of pairs in the list. $\endgroup$ – Edmund Oct 12 '17 at 17:03
  • $\begingroup$ @Edmund See my edit. $\endgroup$ – Alan Oct 12 '17 at 19:39
1
$\begingroup$

Using Outer

n = 0;
f[{}] = {};
f[x_] := Append[x, vals[[++n]]]
Outer[f, res, 2]

{{{20, 1, a}, {200, 2, b}}, {{}}, {{175, 1, c}}}

$\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.