2
$\begingroup$

I'm trying to Reap/Sow elements of a list into separate sub-lists. In this toy example, I want evens in one list, odds in the other. But whichever sub-list d[[1]] happened to belong in, that sub-list comes first in Reap's output. If I wanted the even list to come first, I have to retest and reorder the sub-lists after Reap is finished. That isn't so bad when (as in the example) there are only two sub-lists, but in a bigger problem I have 20+ attributes with corresponding sub-lists! Is there a way to control what order Reap puts sub-lists in?

This works but is clunky and inefficient:

d = RandomInteger[100, 100];  (* Synthetic data *)
r = Reap[Do[If[EvenQ[d[[i]]], Sow[d[[i]], 1], Sow[d[[i]], 2]], {i, 1, Length[d]}]][[2]];
If[! EvenQ[r[[1, 1]]], r = {r[[2]], r[[1]]}]; (* Annoying that I have to do this to make the even list come first. *)
r
$\endgroup$
  • 3
    $\begingroup$ Why not use the 2-argument form of Reap? Reap[ Sow[5, b]; Sow[4, a]; Sow[7, c]; Sow[8, b]; , {a, b, c, d}] for example $\endgroup$ – Jason B. Dec 7 '18 at 19:55
  • $\begingroup$ @JasonB. Thanks. Post this as an Answer, and I'll Accept it. $\endgroup$ – Jerry Guern Dec 7 '18 at 20:20
5
$\begingroup$

The 2-argument form of Reap allows you to control the order of the output:

Reap[
    Sow[5, b];
    Sow[4, a];
    Sow[7, c];
    Sow[8, b];
    5,
    {a, b, c, d}
 ] 
(* {5, {{{4}}, {{5, 8}}, {{7}}, {}}} *)

The 3-argument form gives even more control:

Reap[
    Sow[5, b];
    Sow[4, a];
    Sow[7, c];
    Sow[8, b];
    5,
    {a, b, c, d},
        Rule
 ] 
(* {5, {{a -> {4}}, {b -> {5, 8}}, {c -> {7}}, {}}} *)
$\endgroup$
2
$\begingroup$

Why not use GroupBy instead of Reap/Sow? For your example:

r = GroupBy[d,EvenQ]

<|True -> {80, 14, 0, 100, 68, 74, 24, 4, 100, 90, 70, 30, 48, 44, 56, 28, 68, 26, 68, 10, 86, 76, 44, 86, 18, 38, 30, 54, 4, 6, 2, 34, 18, 6, 6, 30, 60, 80, 56, 50, 58, 94, 92, 88}, False -> {67, 3, 65, 23, 97, 15, 83, 1, 25, 73, 69, 47, 43, 33, 93, 29, 75, 17, 45, 43, 79, 17, 43, 87, 51, 41, 15, 45, 99, 81, 39, 13, 41, 27, 7, 5, 3, 55, 81, 59, 25, 9, 91, 1, 47, 77, 25, 65, 61, 53, 61, 13, 63, 63, 91, 17}|>

If you just want the values, you can do:

Lookup[r, {True, False}]

{{80, 14, 0, 100, 68, 74, 24, 4, 100, 90, 70, 30, 48, 44, 56, 28, 68, 26, 68, 10, 86, 76, 44, 86, 18, 38, 30, 54, 4, 6, 2, 34, 18, 6, 6, 30, 60, 80, 56, 50, 58, 94, 92, 88}, {67, 3, 65, 23, 97, 15, 83, 1, 25, 73, 69, 47, 43, 33, 93, 29, 75, 17, 45, 43, 79, 17, 43, 87, 51, 41, 15, 45, 99, 81, 39, 13, 41, 27, 7, 5, 3, 55, 81, 59, 25, 9, 91, 1, 47, 77, 25, 65, 61, 53, 61, 13, 63, 63, 91, 17}}

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