22

Given

lst = {a, b, c, d}

I'd like to generate

{{a}, {a, b}, {a, b, c}, {a, b, c, d}}

but using built-in functions only, such as Subsets, Partitions, Tuples, Permutations or any other such command you can choose. But it has to be done only using built-in commands. You can use a pure function, if inside a built-in command, i.e. part of the command arguments. That is OK.

It is of course trivial to do this by direct coding. One way can be

lst[[1 ;; #]] & /@ Range[Length[lst]]
(* {{a}, {a, b}, {a, b, c}, {a, b, c, d}}  *)

or even

LowerTriangularize[Table[lst, {i, Length[lst]}]] /. 0 -> Sequence @@ {}
(* {{a}, {a, b}, {a, b, c}, {a, b, c, d}}  *)

But I have the feeling there is a command to do this more directly as it looks like a common operation, but my limited search could not find one so far.

Sorry in advance if this was asked before. Searching is hard for such general questions.

kglr
  • 394,356
  • 18
  • 477
  • 896
Nasser
  • 143,286
  • 11
  • 154
  • 359

11 Answers11

29
 lst={a,b,c,d};
 ReplaceList[lst,{x__, ___} :> {x}]

Speaking of "common operation":

 Table[lst[[;; i]], {i, Length@lst}]
kglr
  • 394,356
  • 18
  • 477
  • 896
  • I was playing with a pattern based solution, +1 for ReplaceList. – image_doctor Dec 18 '12 at 19:27
  • 2
    +1, there's your 20k, btw. I've always had trouble with ReplaceList, and for me it is the second most annoying function that sounds useful. The most annoying being MapAt ... Although, I've more success with MapAt than ReplaceList. – rcollyer Dec 18 '12 at 19:53
  • @rcollyer, @image_doctor, Nasser, thanks for the votes. Rcollyer, for me too -- it always takes several attempts until I get MapAt and ReplaceList right. And, i don't remember ReplaceList winning any speed contests. – kglr Dec 18 '12 at 20:12
  • I found Position is a great way to get MapAt to behave itself, provided you can figure out how to specify what positions you need ... – rcollyer Dec 18 '12 at 20:27
  • 1
    @rcollyer Be careful with MapAt though. – Leonid Shifrin Dec 18 '12 at 20:29
  • @LeonidShifrin absolutely. My primary use has been to change a random element out of a randomly generated list of arbitrary (and random) shape. So, MapAt is useful as I don't need to know how to specify the part correctly, and efficiency takes a back seat to just being able to do it correctly consistently. – rcollyer Dec 18 '12 at 20:37
19

A variant using Take.

list~Take~# & /@ Range@Length@list

{{a}, {a, b}, {a, b, c}, {a, b, c, d}}

One using NestList:

NestList[Most, list, Length@list - 1]

{{a, b, c, d}, {a, b, c}, {a, b}, {a}}

image_doctor
  • 10,234
  • 23
  • 40
17

Subsets takes an optional 3rd argument as Subsets[list, {n}, k] that gives you the kth sublist of length n. Since your sublists are in sequence, you'll always need k = 1. You can then use this as:

MapIndexed[First@Subsets[list, #2, 1] &, list]
(* {{a}, {a, b}, {a, b, c}, {a, b, c, d}} *)

Another alternative would be:

Reverse@Most@NestWhileList[Most, list, # != {} &]
rm -rf
  • 88,781
  • 21
  • 293
  • 472
  • 2
    Why are you using MapIndexed? Why not Subsets[list, #, 1][[1]]& /@ Range[Length@list]? It seems cleaner. – rcollyer Dec 18 '12 at 20:10
  • 2
    "Cleaner" is subjective and in this case, I positively dislike having to use Range@Length@foo when I don't have to. Far cleaner to use MapIndexed and ignore #1 – rm -rf Dec 18 '12 at 20:20
  • +1. But the real clean solution here is to use linked lists, since all of the suggested ones have quadratic complexity in the length of the list. – Leonid Shifrin Dec 18 '12 at 20:28
  • @LeonidShifrin I thought of something like Flatten /@ Rest@FoldList[List, {}, {a, b, c, d}], but it was similar to your solution above and was hoping you'd post it – rm -rf Dec 18 '12 at 20:39
  • FoldList by itself works. – rcollyer Dec 18 '12 at 20:41
  • @rm-rf Somehow I did not expect so much excitement about this topic, so many answers. I could as well post it indeed. Perhaps I will. But my solution suffers from the same flaw as others. – Leonid Shifrin Dec 18 '12 at 21:01
  • @LeonidShifrin My "interest" is in getting a gold badge some day... hopefully without answering too many low hanging fruit :D (damn you for guilt-tripping me :P). But more seriously, I do like list-manipulation questions that are generic (i.e., not overly data specific)... it provides an excellent opportunity to learn different techniques (some that you might not even expect) – rm -rf Dec 18 '12 at 21:02
  • Actually, @rcollyer posted it, so problem solved :). Re: gold badge - I've got one already, on SO :). Seriously, I am much less interested in list manipulations in Mathematica now than I used to be. I found that things that make it hard for me personally to code in M are mostly elsewhere (and it is not always obvious what they are, for a given problem). – Leonid Shifrin Dec 18 '12 at 21:06
  • I think that most of us like list manipulation because M has an outstanding support for them, so that our programming flow can be very smooth. For a number of other things (handling state, implementing data structures with certain characteristics, scaling codebase to larger size, etc), the support is perhaps not so great, so one has to overcome significant mental barriers and these topics are less attractive to most M users. But this is what interests me more at present, since this is what stands in my way. – Leonid Shifrin Dec 18 '12 at 21:17
  • It looks like I need one more question for a copper badge, but a gold one would be nice, too. I guess I need to start answering questions more. – rcollyer Dec 18 '12 at 21:18
  • @LeonidShifrin I need to study up on list manipulation algorithms a bit to consider myself "good" at them. Recursion still drives me a bit nuts. – rcollyer Dec 18 '12 at 21:19
  • @rcollyer I don't consider myself good at them either, just OK. The problem is that Mathematica's execution model often creates rather unnatural preferences for the algorithms implementations. But recursion is very powerful. Actually, I don't know what I would do without it for many problems. It was very important ingredient for RLink, for one thing. – Leonid Shifrin Dec 18 '12 at 21:24
  • I think my objection the the MapIndexed form is that you are carrying around a reference to list, mapping over list, yet only using the indices. So, yes, I think f /@ Range@Length@list is cleaner because it requires less work to understand it quickly. While the MapIndexed form is still quick, it takes more time to parse it mentally. – rcollyer Dec 18 '12 at 21:25
12

I am not sure this wins any speed contests, but it is a purely functional solution:

FoldList[#1~Join~{#2} &, {First@#}, Rest@#]& @ {a, b, c, d, e}
(* {{a}, {a, b}, {a, b, c}, {a, b, c, d}, {a, b, c, d, e}} *)
rcollyer
  • 33,976
  • 7
  • 92
  • 191
  • Ok, since you posted this, I am liberated from doing that (I gave it in comments to the question), and can happily upvote : +1. – Leonid Shifrin Dec 18 '12 at 21:03
  • +1, a variation: FoldList[Flatten@{##} &, {First@list}, Rest@list]? or FoldList[Sequence @@@ {##} &, {First@list}, Rest@list]? – kglr Dec 18 '12 at 21:06
  • @kguler I'd migrate list outside of FoldList, but I hate writing more than I have too. – rcollyer Dec 18 '12 at 21:15
  • @LeonidShifrin teach me to look at the comments before I post something ... Speed wise, though, is Join or Append faster. What about kguler's alternatives? – rcollyer Dec 18 '12 at 21:16
  • I think @kguler's use of ReplaceList is very elegant. For generic lists, should be quite fast. For packed arrays, Join and Append should be faster, since ReplaceList can not make use of those and will likely unpack. – Leonid Shifrin Dec 18 '12 at 21:20
  • @LeonidShifrin I think ReplaceList is a brilliant use of that function. But, I had more in mind the speed differences between the FoldList implementations. – rcollyer Dec 18 '12 at 21:22
  • @rcollyer I did not benchmark (FoldList), but I don't expect them (differences) to be very significant. – Leonid Shifrin Dec 18 '12 at 21:26
10

What about Accumulate:

Function[lst, {{lst[[1]]}}~Join~Rest[Accumulate[lst] /. Plus -> List]]@{a, b, c, d, e}

Unfortunately it doesn't accept a custom function other than Plus and will not work for numerical list...

Silvia
  • 27,556
  • 3
  • 84
  • 164
6

A variant using Partition:

First[Partition[list,#]]& /@ Range@Length@list
Stefan
  • 5,347
  • 25
  • 32
5
FromLetterNumber@Range@Range[4]

{{a},{a,b},{a,b,c},{a,b,c,d}}

Edit

On looking at this answer by Carl Woll (see also part-2 of the answer by image_doctor below).

And see also the comment by kglr to this answer by rcollyer.

FoldList[Flatten[{##}] &, Nothing, {a, b, c, d}]

{{a}, {a, b}, {a, b, c}, {a, b, c, d}}

user1066
  • 17,923
  • 3
  • 31
  • 49
5

A joke solution:

Outer[Take, {{a, b, c, d, e}}, Range[5], 1] // First
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
3

This is not a built-in function to do it but it fits the criteria of only using buil-in functions. It avoids using patterns, mapping constructs and such things.

Maybe in the future ListCorrelate can accepts functions instead of heads (e.g. applying Plus to a list by default). I think that would make it more useful (but I am a beginner Mathematica user, so who am I to hold such opinions).

lst = {a, b, c, d};
DeleteCases[
 ListCorrelate[lst, ConstantArray[1, Length@lst], 1, 0, Times, 
  List], 0, {2}]
C. E.
  • 70,533
  • 6
  • 140
  • 264
2
list = {a, b, c, d};

MapAt[List, 1] @ MapApply[List] @ Accumulate @ list

{{a}, {a, b}, {a, b, c}, {a, b, c, d}}

MapApply came with V 13.1

eldo
  • 67,911
  • 5
  • 60
  • 168
1

Using Cases and Accumulate:

list = {a, b, c, d};
Cases[Accumulate[list], s_ :> If[AtomQ[s], {s}, List @@ s]]

({{a}, {a, b}, {a, b, c}, {a, b, c, d}})

Also, using Drop:

Reverse[Table[Drop[#, -i], {i, 0, Length@# - 1}]] &@list

({{a}, {a, b}, {a, b, c}, {a, b, c, d}})

Also, usin TakeList:

Table[Sequence @@ TakeList[#, {i}], {i, Length@#}] &@list

({{a}, {a, b}, {a, b, c}, {a, b, c, d}})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44