25

How to make a function that splits list elements by odd and even positions? Shortest implementation wins. I myself came up with:

splitOdds[x_] := 
 Extract[x, {#}\[Transpose]] & /@ GatherBy[Range@Length@x, OddQ]

And:

splitOdds[x_] := Flatten[Partition[#, 1, 2]] & /@ {x, Rest@x}

splitOdds[{a, b, c, d, e, f}]
(*{{a, c, e}, {b, d, f}}*)
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
swish
  • 7,881
  • 26
  • 48

11 Answers11

22

A couple for fun:

lst = {a, b, c, d, e, f, g};

Partition[lst, 2, 2, 1, {}] ~Flatten~ {2}
{{a, c, e, g}, {b, d, f}}
i = 1; GatherBy[lst, i *= -1 &]
{{a, c, e, g}, {b, d, f}}

And my Golf entry:

lst[[# ;; ;; 2]] & /@ {1,2}
{{a, c, e, g}, {b, d, f}}

And here is an anti-Golf "Rube Goldberg" solution:

ReleaseHold[List @@ Dot @@ 
  PadRight[{Hold /@ lst, {}}, Automatic, #]] & /@
    Permutations[Range[1, 0, -1]]
{{a, c, e, g}, {b, d, f}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
21

Less than sensible, more than pretty, hopefully enjoyable, with a different notion of grouping. Based in part on this question. Gives a new meaning to bubble sort.

Movie of grouping Movie of grouping

list = {a, b, c, d, e, f, g(*, h, l, m, n, o, p, q, r, s, t*)};

likeElements[list_, {idx_}] /; OddQ[idx] := list[[1 ;; ;; 2]]; 
likeElements[list_, {idx_}] /; EvenQ[idx] := list[[2 ;; ;; 2]];
coords = Transpose[{Range[#], RandomReal[{-0.01, 0.01}, #], RandomReal[{-0.01, 0.01}, #]}] &@Length[list]; 

Dynamic[
 Refresh[Module[{d},
   Graphics3D[GraphicsComplex[
     coords -= MapIndexed[
       Total[Function[x, (d = # - x)/(d = Sqrt[d.d]) Log@d/2^(2 + 2 Sqrt[d])] /@ 
           Drop[likeElements[coords, #2], Ceiling[#2/2]]] + 
         Total[Function[x, -(d = # - x)/(d = Sqrt[d.d])^2 (d - 1/E) (1 - d/7)/2^(2 + 2 d)] /@ 
           likeElements[coords, #2 + 1]] &, coords],
    {MapIndexed[Text[Style[#1, ColorData[2][Mod[First[#2], 2]]], First[#2]] &, list],
     Opacity[0.3], Sphere[Range@Length[list], E^-1]}], 
    PlotRange -> {{-1.5, Length[list] + 1.5}, 4 {-1, 1}, 4 {-1, 1}}]
   ],
  UpdateInterval -> 1]]

Tweaking the coefficients slightly changes the behavior, which is also somewhat dependent on the length of the list. Won't win a speed contest.

The two Total[Function...] expressions calculate the new positions based on like elements (same parity) attract (first Total) and unlike repel (second Total).

Michael E2
  • 235,386
  • 17
  • 334
  • 747
17

You can also use Downsample, which is new in version 9:

lst = {a, b, c, d, e, f, g};
Downsample[lst, 2, #] & /@ {1, 2}
(* {{a, c, e, g}, {b, d, f}} *)
rm -rf
  • 88,781
  • 21
  • 293
  • 472
Rojo
  • 42,601
  • 7
  • 96
  • 188
13

Since all the sensible answers have already been done...

lst ~(•=#;Cases)~(_/;(•=!•))&/@{!#,#}&[_==_]
Simon Woods
  • 84,945
  • 8
  • 175
  • 324
10

My way:

lst = {a, b, c, d, e, f, g};    

Take[lst, {#, -1, 2}] & /@ {1, 2}
{{a, c, e, g}, {b, d, f}}
VLC
  • 9,818
  • 1
  • 31
  • 60
6

Here is another one, based on Reap and Sow:

Reap[MapIndexed[Sow[#1, Mod[#2, 2]] &, lst], _, #2 &][[2]]

This one has an advantage to be easily generalizable to more complex conditions, although certainly not the fastest one here.

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • That looks too complicated. I would write: Reap[# ~Sow~ Mod[#2, 2] & ~MapIndexed~ lst][[2]] – Mr.Wizard Mar 16 '13 at 13:54
  • @Mr.Wizard Yes, you can skip the pattern on Reap. But, generally, I am in the habit of keeping it, since I often use Reap with some tags just for safety. – Leonid Shifrin Mar 16 '13 at 14:25
6

I'll join in with my own version:

splitList[list_] := Pick[list, 
    IntegerDigits[1/6 (-3 - (-1)^#1 + 2^(2 + #1)) &@Length@list, 2], #] & /@ {1, 0}

splitList[{a, b, c, d, e, f, g}]
(* {{a, c, e, g}, {b, d, f}} *)

This uses the fact that the "selector pattern" or "sieve" for the elements proceeds as

$$1, 10, 101, 1010, 10101,... $$

and the general term (in base 10) for the binary sequence above is $\frac{1}{6}(2^{2+n}-(-1)^n-3)$, where $n$ is the length of your list.

The selector pattern can also be generated more straightforwardly as:

Riffle[ConstantArray[1, Ceiling[Length@list/2]], 0]
rm -rf
  • 88,781
  • 21
  • 293
  • 472
5

Method 1: GatherBy each element's position (even or odd).

GatherBy[lst, Mod[Position[lst,#],2]&]

{{a, c, e, g}, {b, d, f}}


Method 2: Using ArrayReshape (version 9).

Mathematica graphics

In the following, the MathematicaIcon is used for padding in the reshaping of the array. After the array is reshaped, the icons are removed. Any element can be used in lieu of the MathematicaIcon, provide that one is certain that the padding element is not in the original list.

 ArrayReshape[lst,{Length[lst],2},"\[MathematicaIcon]"]\[Transpose]
 /."\[MathematicaIcon]"->Sequence[]

Method 3: Check whether each index from MapIndexed is even or odd.

GatherBy[MapIndexed[List,lst],OddQ]/.{x_,{_}}:> x
DavidC
  • 16,724
  • 1
  • 42
  • 94
4

Base on Pick:

gather[list_] := Pick[{list, list}, Take[#, Length@list] & /@ {#, RotateLeft[#]} &@
  Mod[Range@Ceiling[Length@list, 2], 2], 1];

gather[{a, b, c, d, e, f, g}]
{{a, c, e, g}, {b, d, f}}
Michael E2
  • 235,386
  • 17
  • 334
  • 747
2
# & @@@ # & /@ GatherBy[MapIndexed[{#, OddQ@#2} &, lst], Last]
#[[All, 1]] & /@ GatherBy[MapIndexed[{#, OddQ@#2} &, lst], Last]
chyanog
  • 15,542
  • 3
  • 40
  • 78
0

This is how i do it:

GetEvenStep[list_] := Partition[list, 2][[All, 1]]

GetOddStep[list_] := Partition[list, 2][[All, 2]]

Be careful, depending on the list you want to split you might want to flip the names!