I have an array of arbitrary elements, say strings:

testArray = {"String A", "String B", "String C", "String D", "String E"};

How can I split this into two arrays, one for the element with odd indices in testArray and one for elements with even indices in testArray:

testArrayOdd = {"String A", "String C", "String E"};
testArrayEven = {"String B", "String D"};

Is there a simple way to do this that generalizes to more complicated partitionings based on, say, the primeness of the index?

Clarification!: The strings in the array can be anything "String 1" could be "knejibvei (junk)". We only care about the INDEX of the string in the original array. Sorry about this. For odd / even testing of elements, see here: Separate an array in two arrays, the even and odd terms being separated in these two arrays.


4 Answers 4


For the simple case of even and odd, you can do either:

{testArrayOdd, testArrayEven} = {testArray[[;; ;; 2]], testArray[[2 ;; ;; 2]]}
(* {{"String 1", "String 3", "String 5"}, {"String 2", "String 4"}} *)


{testArrayOdd, testArrayEven} = Partition[testArray, 2, 2, 1, {}] ~Flatten~ {2}

For a more general grouping based on an arbitrary predicate, you can write a custom function:

groupByPositions[list_, pred_] := 
    With[{trueList = testArray[[Select[Range@Length@list, pred]]]},
        {trueList, Complement[list, trueList]}

With this, the original problem becomes

{testArrayOdd, testArrayEven} = groupByPositions[testArray, OddQ]

and to group by prime indices:

groupByPositions[testArray, PrimeQ]
(* {{"String 2", "String 3", "String 5"}, {"String 1", "String 4"}} *)
  • $\begingroup$ Congratulations on the list-manipulation gold badge! $\endgroup$
    – Artes
    Apr 4, 2014 at 14:44
  • $\begingroup$ I didn't even realize you could use the third argument of Flatten that way. Mind == blown. $\endgroup$
    – Pillsy
    Apr 4, 2014 at 17:50
  • $\begingroup$ Looks like you covered everything, and you even threw in some ~infix~ for me. +1 $\endgroup$
    – Mr.Wizard
    Apr 4, 2014 at 19:29
  • $\begingroup$ in your GroupByPositions code testArray should be 'list'. $\endgroup$
    – ogerard
    Dec 26, 2017 at 10:25

Just for the sake of variety:

MapIndexed could be used here, in conjunction with tagged Sowing and Reaping:

Reap[MapIndexed[If[PrimeQ@First@#2, Sow[#1, 1], Sow[#1, 2]] &, 
   testArray]] // Last (* partition based on index primeness *)

(* {{"String 1", "String 4"}, {"String 2", "String 3", "String 5"}} *)

I've used the integers 1 and 2 as tags. (The docs show symbols like x and y being used, but integers appear to work OK too.)

The list associated with the first tag to be encountered is given first, hence the order of the sublists above. If you wished to change that, you could add {2, 1} as an extra argument at the end of Reap.

(As Mr. Wizard suggests in his comment, a better expression is:

Reap[MapIndexed[# ~Sow~ PrimeQ[#2] &, testArray]][[2]]

where the tag is automatically generated as the predicate's value and the If is completely avoided.)

Personally I find GatherBy most natural here:

Part[testArray, #] & /@ GatherBy[Range@Length@testArray, PrimeQ]
  • $\begingroup$ Thanks, I'm getting the impression that Sow and Reap are really worth learning about. $\endgroup$
    – CA30
    Apr 4, 2014 at 16:32
  • $\begingroup$ Personally I feel that Sow and Reap are a bit of a throwback to procedural programming - not to say there aren't certain situations where they're useful, or that they aren't worth adding to your Mathematica toolbox. $\endgroup$
    – Aky
    Apr 4, 2014 at 17:15
  • $\begingroup$ @CA30 In my opinion they are very worth learning about due to the flexibility of the abstraction and it's relatively high performance. While e.g. GatherBy (added to the language after Sow/Reap) should be used where easily applicable as it will be both more concise and somewhat faster, Sow and Reap let you do things not otherwise possible without resorting to more verbose methods. $\endgroup$
    – Mr.Wizard
    Apr 4, 2014 at 19:33
  • $\begingroup$ I can only comment that anything Mr.Wizard has to say about Mathematica is worth heeding! $\endgroup$
    – Aky
    Apr 4, 2014 at 19:51
  • $\begingroup$ Thanks, Aky. Incidentally I would write the Sow/Reap method like this: Reap[MapIndexed[# ~Sow~ PrimeQ[#2] &, testArray]][[2]]. You can also declare a specific order of output using the second parameter of Reap, e.g.: Reap[MapIndexed[# ~Sow~ PrimeQ[#2] &, testArray], {True, False}][[2, All, 1]]. (This returns the prime-index elements in the first list rather than the second.) $\endgroup$
    – Mr.Wizard
    Apr 4, 2014 at 20:10

You could use my GatherByList function to do this:

GatherByList[list_, representatives_] := Module[{func},
    func /: Map[func, _] := representatives;
    GatherBy[list, func]

GatherByList[testArray, EvenQ[Range @ Length @ testArray]]
GatherByList[testArray, PrimeQ[Range @ Length @ testArray]]

{{"String A", "String C", "String E"}, {"String B", "String D"}}

{{"String A", "String D"}, {"String B", "String C", "String E"}}

ClearAll[groupByIndex1, groupByIndex2]
groupByIndex1 = Function[{t}, Pick[#, #2 /@ (Range @ Length @ #), t]] /@ {True, False} &;

groupByIndex2 = Function[{d, f}, Values@Reverse@ KeySort @ 
  GroupBy[MapIndexed[{#, #2[[1]]} &, d], f[Last@#] & -> First]];


groupByIndex1[testArray, OddQ]

{{"String A", "String C", "String E"}, {"String B", "String D"}}

groupByIndex1[testArray, EvenQ]

{{"String B", "String D"}, {"String A", "String C", "String E"}}

groupByIndex1[testArray, PrimeQ]

{{"String B", "String C", "String E"}, {"String A", "String D"}}

groupByIndex1[testArray, 2 < # <= 4 &]

{{"String C", "String D"}, {"String A", "String B", "String E"}}

And @@ (groupByIndex1[testArray, #] == 
        groupByIndex2[testArray, #] & /@ {OddQ, EvenQ, PrimeQ, 2 < # <= 4 &})



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.