Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying transformation of `GatherBy` to a different list

I have listA and listB of the same size. I'm doing GatherBy on listA, which rearranges that list. What is an elegant way to apply identical rearrangement to listB?

For example

listA = {1, 2, 3};
listB = {a, b, c};
listA1 = GatherBy[{1, 2, 3}, OddQ];

listB1 should become {{a, c}, {b}}

Update Thanks for interesting ideas, I eventually ended up doing something similar to belisarius. This reminds me of Python's "decorate-sort-undecorate" pattern

decorated = Thread[{listA, listB}];
grouped = GatherBy[decorated, OddQ[First[#]] &];
listB1 = Map[Last, grouped, {2}]
like image 401
Yaroslav Bulatov Avatar asked Jan 26 '11 22:01

Yaroslav Bulatov


3 Answers

Well, first second try:

(Warning Warning ... "elegance" is an utterly subjective concept)

gBoth[lslave_, lmaster_, f_] := 
                 {Part[#, All, All, 1], Part[#, All, All, 2]} &@ 
                 GatherBy[Transpose[{lslave, lmaster}], f[#[[2]]] &]

lmaster = {1, 2, 3};
lslave = {a, b, c};  

{lslave1, lmaster1} = gBoth[lslave, lmaster, OddQ]  

Out

{{{a, c}, {b}}, {{1, 3}, {2}}}  

Edit

Note that for this code to run you must have

 Dimensions[lslave][[1;;Length[Dimensions@lmaster]]] == Dimensions@lmaster  

but the deeper internal structure of both lists could be different. For example:

lmaster = {{1, 2, 3}, {2, 3, 4}};
lslave = {{{a}, {b}, {c}}, {{a}, {b}, {c}}};

{lslave1, lmaster1} = gBoth[lslave, lmaster, #[[1]] < 3 &]

Out

{{{{{a}, {b}, {c}}, {{a}, {b}, {c}}}}, {{{1, 2, 3}, {2, 3, 4}}}}

HTH!

like image 164
Dr. belisarius Avatar answered Sep 18 '22 14:09

Dr. belisarius


How about

Map[listB[[#]] &, listA1 /. Dispatch@Thread[listA -> Range[Length[listA]]]]

Edit : It actually came to my mind that this solution will have problems if listA has repeated elements.Besides, it uses the specialized knowledge that the resulting list is of constant depth 2. Here is a more general (admittedly, ugly) version, which does not care what is the resulting list structure, or whether the original list did have repeated elements :

Clear[rearrangeAs];
rearrangeAs[source_List, transformed_List, target_List] := 
  Module[{f, count, symbs = Table[Unique[], {Length[source]}]}, 
    count[_] = 0;
    f[x_, _] := x;
    MapThread[With[{cnt = ++count[#1]}, f[#1, cnt] := #2] &, {source, symbs}];
    Clear[count];
    count[_] = 0;
    Replace[transformed, x_ :> f[x, ++count[x]], {0, Infinity}] /. 
       Dispatch[Thread[symbs -> target]]]

For example,

In[94] := rearrangeAs[listA, listA1, listB]

Out[94] = {{a, c}, {b}}

I did not test, but this function should also work when the transformed list does not have a regular structure, but is some general tree

like image 32
Leonid Shifrin Avatar answered Sep 19 '22 14:09

Leonid Shifrin


You essentially want:

Map[listB[[#]] &, listA1]

Since ListB[[{1,3,5}]] for example gives a list of the first, third, and fifth elements of ListB.

So this a very simple version of the function:

example[listA_, listB_, ordering_] := 
 Map[listB[[#]] &, GatherBy[listA, ordering]]

Its important to note that if a number is duplicated in ListA then it won't appear because of the behavior of GatherBy:

example[{1, 2, 3, 4, 5, 6, 3, 5}, {a, b, c, d, e, f, g, h}, OddQ]

{{a, c, e, c, e}, {b, d, f}}
like image 40
Searke Avatar answered Sep 18 '22 14:09

Searke