12
$\begingroup$

I have a list as follows

lis= {a, {{{b}, {c,d,e}}, {{f}, {g,h,i}}}}

which I would like to flatten to

flattenLis={a,{b,c,d,e},{f,g,h,i}}

Does anyone have a hint. All my flatten attempts did not work. Thanks

$\endgroup$
11
$\begingroup$

Here is an approach where I apply the flattening "from the bottom up" (negative level specification). This works by operating on {lis} instead of lis, in order to have sufficiently many levels even when there's no list wrapping an element, such as a:

Flatten[Map[Flatten, {lis}, {-3}], 2]

(* ==> {a, {b, c, d, e}, {f, g, h, i}} *)

My approach also works for this example:

lis2 = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}}}, b};

Flatten[Map[Flatten, {lis2}, {-3}], 2]

(* ==> {a, {b, c, d, e}, {f, g, h, i}, b} *)
$\endgroup$
  • $\begingroup$ According your idea, {#1, Sequence @@ #2} & @@ Map[Flatten, lis, {-3}] also works:) $\endgroup$ – xyz Mar 24 '15 at 1:39
  • $\begingroup$ There is a potential problem with this approach. This will fail if the elements are not atomic, e.g. if b = Sqrt[2];. My method does not fail in that circumstance, nor does it fail on lis2 despite your assertion. $\endgroup$ – Mr.Wizard Mar 24 '15 at 3:46
  • $\begingroup$ @Mr.Wizard Yes, the bottom-up approach can only work if the leafs are atomic - I guess maybe one could ensure that using Block if the entries are at least named by symbols that can be blocked. $\endgroup$ – Jens Mar 24 '15 at 4:19
  • 1
    $\begingroup$ @ShutaoTang Yes, but your Slot based approach wouldn't work with my lis2. Anyway, at this point the OP certainly has a lot of methods to choose from... $\endgroup$ – Jens Mar 24 '15 at 4:31
10
$\begingroup$

Not sure how general it will be:

{#, ## & @@ Flatten /@ #2} & @@ lis
{a, {b, c, d, e}, {f, g, h, i}}
$\endgroup$
  • 2
    $\begingroup$ Same comment here as for @Mr.Wizard. $\endgroup$ – Jens Mar 23 '15 at 22:50
  • $\begingroup$ Could you @Kuba give some explanation here? :). $\endgroup$ – xyz Mar 24 '15 at 1:19
  • $\begingroup$ I see. 1) ` {#1, Flatten /@ #2} & @@ lis(==>{a, {{b, c, d, e}, {f, g, h, i}}})` 2) {#, ## & @@ #2} & @@ % (*==> {a, {b, c, d, e}, {f, g, h, i}}*) $\endgroup$ – xyz Mar 24 '15 at 5:00
  • $\begingroup$ @Jens sure but that is not the question. :) $\endgroup$ – Kuba Mar 24 '15 at 6:58
8
$\begingroup$

One approach:

MapAt[Flatten, lis, {2, All}] ~FlattenAt~ 2
{a, {b, c, d, e}, {f, g, h, i}}

Hopefully some combination of MapAt, Flatten, and FlattenAt will work for any structure you have.

$\endgroup$
  • $\begingroup$ This works for the example, but not for lis = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}}}, b}. I upvoted anyway, since the question doesn't specify what generalizations are desirable. $\endgroup$ – Jens Mar 23 '15 at 22:49
  • $\begingroup$ I discovered that this solution was restricted in V8.MapAt::psl: "Position specification {2,All} in MapAt[Flatten,{a,{{{b},{c,d,e}},{{f},{g,h,i}}}},{2,All}] is not an integer or a list of integers. " $\endgroup$ – xyz Mar 24 '15 at 1:26
  • $\begingroup$ @ShutaoTang That is correct; support for All and Span within MapAt was (silently) introduced in version 9; see: (31173). There are alternatives in that Q&A that will work in earlier versions. $\endgroup$ – Mr.Wizard Mar 24 '15 at 3:39
  • $\begingroup$ @Jens I'm not sure what you mean; using your lis I get {a, {b, c, d, e}, {f, g, h, i}, b} -- what else do you want? $\endgroup$ – Mr.Wizard Mar 24 '15 at 3:40
  • $\begingroup$ @Mr.Wizard Oh, I know what happened: I was on MMA version 8, and tested my lis2 with your method. It errored out, but now I see it works in version 10. In version 8, your answer doesn't even work with the original lis... It's because MapAt in version 8 doesn't seem to accept position specification All. That change isn't mentioned in the version-10 documentation. $\endgroup$ – Jens Mar 24 '15 at 4:12
7
$\begingroup$

Also:

lis = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}}}};
Fold[Apply[## &, #, {#2}] &, lis, {1, 2}]
(* {a, {b, c, d, e}, {f, g, h, i}} *)

And ... ♯ = {## & @@ #, ## & @@@ #2} & @@ ({##}) &'s close relative:

♭ = ## & @@@ (## & @@@ {## & @@@ # & /@ #} & /@ #) &;

Examples:

♭ @ lis
(* {a, {b, c, d, e}, {f, g, h, i}} *)

lis2 = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}, {x}}}, b};
♭ @ lis2
{a, {b, c, d, e}, {f, g, h, i, x}, b}
$\endgroup$
  • 1
    $\begingroup$ Your last one is probably a quote from the pirate in Asterix&Obelix, right? But it doesn't work on lis = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}}}, b}. $\endgroup$ – Jens Mar 23 '15 at 22:40
  • $\begingroup$ @Jens, right! and Getafix says there is a fix:) $\endgroup$ – kglr Mar 23 '15 at 23:17
  • $\begingroup$ If anyone looks at the edit history, they will get dizzy... I could still annoy you with this example, though: lis = {a, {{{b}, {c, d, e}}, {{f}, {g, h, i}, {x}}}, b}; where I added an {x} together with the {f}. Your first approach works, the second still doesn't. $\endgroup$ – Jens Mar 23 '15 at 23:22
  • $\begingroup$ @Jens, deleted the quotes from the pirate and getafix:) Actually, i am surprised the first one works beyond op's simple example. $\endgroup$ – kglr Mar 23 '15 at 23:28
  • $\begingroup$ I didn't really intend to make you delete the other solution - it was fun to look at... $\endgroup$ – Jens Mar 24 '15 at 16:53
6
$\begingroup$

Another approach is rule replacement, which can be restricted to a specific level.

In[16]:=Replace[lis, l_List :> Sequence @@ Flatten[l], {1}] === flattenList

Out[16]=True

I didn't see an easy way to make this one work without Sequence.

$\endgroup$
3
$\begingroup$

I'm not sure I really understand how the example is to be generalised, but this removes all List heads at level 2 and 4:

ReplacePart[lis, Position[lis, List, {#}] & /@ Join[2, 4] -> Sequence]

(* {a, {b, c, d, e}, {f, g, h, i}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.