Gathering a List efficiently
SeedRandom[123]
L = Table[RandomChoice[{1, 4} -> {{}, RandomChoice[CharacterRange["A", "G"],
RandomChoice[{2, 4, 6, 8}]]}], {15}]
{{"G", "E", "F", "C"}, {"G", "D", "B", "C", "D", "A"}, {"D", "B", "E", "E", "F", "D", "G", "G"}, {}, {"C", "C", "F", "A", "D", "C"}, {"B", "A", "G", "B"}, {"A", "E"}, {}, {}, {"C", "F"}, {"F", "E", "C", "E"}, {"G", "E"}, {"C", "E", "C", "A", "B", "A"}, {"G", "F", "G", "F", "D", "B", "A", "B"}, {"A", "C", "B", "E"}}
Q = Length;
{L0, L2, L4, L6, L8} = Pick[Range @ Length @ L, #- Q /@ L, 0] & /@ {0, 2, 4, 6, 8}
{{4, 8, 9}, {7, 10, 12}, {1, 6, 11, 15}, {2, 5, 13}, {3, 14}}
Or, as suggested by Kuba in comments:
{L0, L2, L4, L6, L8} = PositionIndex[Q /@ L] /@ {0, 2, 4, 6, 8} /. _Missing-> {}
same output
Pulling my approach out from the comments:
lists = {{1, 2}, {3, 4, 5, 6}, {7, 8, 9, 10}, {11, 12}};
func = Length;
values = {0, 2, 4, 6, 8};
Lookup[PositionIndex[func /@ lists], values, {}]
{{}, {1, 4}, {2, 3}, {}, {}}
I thought kglr's approach would be pretty fast (as Pick
often is), but since we're dealing with unpacked arrays, it took a lot of special effort to get the most out of it. Here's a performance comparison.
SeedRandom[123]
lists = Table[
RandomChoice[{1, 4} -> {{},
RandomChoice[CharacterRange["A", "G"],
RandomChoice[{2, 4, 6, 8}]]}], {100000}];
values = {0, 2, 4, 6, 8} // Developer`ToPackedArray;
(r1 = Pick[ConstantArray[Range@Length@lists, Length@values],
ConstantArray[
func /@ lists // Developer`ToPackedArray,
Length@values
] - values, 0
]); // AbsoluteTiming (* my attempt to vectorize kglr's solution *)
(* kglr's solution as is *)
(r2 = Pick[Range@Length@lists, # - func /@ lists, 0] & /@ values); // AbsoluteTiming
(* my approach *)
(r3 = Lookup[PositionIndex[func /@ lists // Developer`ToPackedArray], values, {}]); // AbsoluteTiming
r1 === r2 === r3
{0.0450857, Null} {0.122854, Null} {0.0340838, Null} True
I suggest that instead of {L0, L2, L4, L6, L8}
you use {lq[0], lq[2], lq[4], lq[6], lq[8]}
.
My approcach
lq = GroupBy[
MapIndexed[{##} &, L]
, Q@*First -> Last@*Last
]
But I have to admit that @Kuba's is the best solution to my taste
lq = PositionIndex[Q /@ L]