While partitioning the elements in a list using GatherBy, can I correspondingly partition the elements of an unrelated list?
Here is another approach. The basic idea is that GatherBy
creates a list of representatives corresponding to the input, then partitions the input based on those representatives. We can see this in action with Trace
. Here is an example:
func[x_] := Mod[x, 3]
Trace[GatherBy[{1, 2, 3, 4, 5}, func], TraceInternal->True]
{GatherBy[{1,2,3,4,5},func],{func/@{1,2,3,4,5},{func[1],func[2],func[3],func[4],func[5]},{func[1],Mod[1,3],1},{func[2],Mod[2,3],2},{func[3],Mod[3,3],0},{func[4],Mod[4,3],1},{func[5],Mod[5,3],2},{1,2,0,1,2}},{{1,4},{2,5},{3}}}
Notice how GatherBy
internally computes func /@ {1,2,3,4,5}
to create the list of representatives {1, 2, 0, 1, 2}
. This list of representatives is then used to partition the input. So, to answer your question, we can override the Map
so that it uses an alternate list of representatives. Here is a function that does this:
GatherByList[list_, representatives_] := Module[{func},
func /: Map[func, _] := representatives;
GatherBy[list, func]
]
For your example:
GatherByList[list2, Mod[list1, 3]]
{{"It", "the", "man", "shall"}, {"is", "morning", "complained", "consider"}, {"only", "the", "I", "nothing"}}
Let's compare timings with the accepted answer:
list1 = Range[10^6];
list2 = RandomReal[1, 10^6];
r1 = GatherByList[list2, Mod[list1, 3]]; //RepeatedTiming
r2 = GatherBy[Transpose[{list1, list2}], Mod[#[[1]], 3] &][[All, All, 2]]; //RepeatedTiming
r1 === r2
{0.013, Null}
{1.36, Null}
True
So, GatherByList
is 2 orders of magnitude faster.
list1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
list2 = {"It", "is", "only", "the", "morning", "the", "man", "complained", "I", "shall",
"consider", "nothing"};
GatherBy[Transpose[{list1, list2}], Mod[#[[1]], 3] &][[All, All, 2]]
(* {{"It", "the", "man", "shall"}, {"is", "morning", "complained", "consider"},
{"only", "the", "I", "nothing"}} *)
list1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
idx = GatherBy[list1, Mod[#, 3] &]
list2 = {"It", "is", "only", "the", "morning", "the", "man", "complained",
"I", "shall", "consider", "nothing"};
Part[list2, #] & /@ idx