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

5 Answers 5

11
$\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$
6
  • $\begingroup$ Block with PreIncrement and that negative Replace level spec. You are too clever! (1+) while we wait. $\endgroup$
    – Edmund
    Oct 12, 2017 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, 2017 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, 2017 at 3:48
  • $\begingroup$ @march, being "chastised"... it does ring a bell:) $\endgroup$
    – kglr
    Oct 12, 2017 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, 2017 at 4:05
2
$\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$
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$
4
  • $\begingroup$ Thanks, but I only want to call RandomVariate once. $\endgroup$
    – Edmund
    Oct 12, 2017 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, 2017 at 16:24
  • $\begingroup$ By the number of pairs in the list. $\endgroup$
    – Edmund
    Oct 12, 2017 at 17:03
  • $\begingroup$ @Edmund See my edit. $\endgroup$
    – Alan
    Oct 12, 2017 at 19:39
1
$\begingroup$
 Module[{i = 1}, res /. {a__?NumberQ} :> {Splice[{a}], vals[[i++]]}]

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

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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