Mathematica, 16 14 bytes
{##&@@#&//@#}&
An unnamed function which takes and returns a list, e.g.:
{##&@@#&//@#}& @ {{{20}, {"Hi"}, "Hi", 20}}
(* {20, "Hi", "Hi", 20} *)
Explanation
Syntactic sugar party!
To understand how this works, note that every expression in Mathematica is either an atom (e.g. numbers, strings, symbols) or a compound expression of the form f[a, b, c, ...]
, where f
, a
, b
, c
are themselves arbitrary expressions. Here, f
is called the head of the expression. Everything else on top of that is just syntactic sugar. E.g. {a, b, c}
is just List[a, b, c]
.
We start with //@
which maps a functions over all levels of a list. For instance:
f //@ {{{20}, {"Hi"}, "Hi", 20}}
(* f[{f[{f[{f[20]}], f[{f["Hi"]}], f["Hi"], f[20]}]}] *)
Note that this maps f
over atoms as well as compound expressions. What we're now looking for is a way to get rid of the list heads and keep everything else.
The Apply
function is normally used to feed the elements of a list as separate arguments to a function, but its actual definition is more general and simply replaces the head of an expression. E.g. Apply[g, f[a, b]]
gives g[a, b]
.
Now there's a special "head" called Sequence
that simply vanishes. E.g. {a, Sequence[b, c], d}
just evaluates to {a, b, c, d}
. The idea for flattening the list is to replace the heads of all inner lists with Sequence
so that they get splatted into their surrounding list. So what we want is to Apply
the head Sequence
to the lists. Conveniently if we Apply
something to an atom, it just leaves the atom unchanged, so we don't have to distinguish between types of expressions at all.
Finally, there's one small issue: f
is also applied to the outermost level, so that it also removes the outermost List
, which we don't want. The shortest way to counter that is simply to wrap the result in a list again, such that the surrounding Sequence
can safely vanish.
Note that there's neither Apply
nor Sequence
in the code. @@
is an operator form of Apply
and ##&
is a standard golfing trick to shorten the long built-in name Sequence
. So ungolfing everything a bit, we get something like:
flatten[list_] := { MapAll[Apply[Sequence], list] }
For more details on how and why the ##&
works, see the section on "Sequences of arguments" in my answer for the Mathematica tips.
7Some languages treat strings as arrays, is [["Hi"],[[10]]] -> ["H","i",10] ok? – Adám – 2016-05-18T05:30:26.580
@NᴮᶻNo, I'm afraid that isn't OK. – Arjun – 2016-05-18T05:37:49.843
1I'm really surprised this isn't a duplicate – Mego – 2016-05-18T06:10:00.297
4@Mego I was surprised too to find out that there was an
unflatten
question but noflatten
question on PPCG. – Arjun – 2016-05-18T06:13:29.240Is it okay if the output is an array of all strings, but still contain the same values in the same order as the input array? For instance, is it okay if the input
[[[20],["Hi"],"Hi",20]]
results in the output['20', "'Hi'", "'Hi'", '20']
? – R. Kap – 2016-05-18T09:09:46.1773What if your language only supports subarrays of the same size? (E.g. Java?) What if the type of each element must be the same? (E.g. Java, C++ etc.?) Also, please add e.g.
["[",[["[",],'[',"['['"]]
as a test case. – flawr – 2016-05-18T11:30:54.9404@flawr That test case only makes sense for languages that support bot
'
and"
as delimiters. (But I agree that a test case involving[
,]
,"
and\
inside a string would be useful.) – Martin Ender – 2016-05-18T11:40:37.1674The test cases also exclude languages which do not support these kinds of arrays with multiple types, or with another notation for array literals. – flawr – 2016-05-18T11:42:39.567
@R.Kap Sorry but that's not allowed. I've edited the question also. – Arjun – 2016-05-18T12:00:34.690
3@flawr Of course, but I think we always assume that you just rewrite the test cases with your language's syntax. Making using of something like redundant literal syntax that is only present in some languages seems unnecessarily specific. – Martin Ender – 2016-05-18T12:00:37.823
2@flawr Java can have subarrays of different sizes (but not different types, thus not different 'depths'). – David Conrad – 2016-05-19T06:33:56.590
3@DavidConrad not true - an array is an
Object
, so anObject[]
can contain other arbitraryObject[]
. Obviously this isn't typesafe, and any algorithm flattening anObject[]
would need to use reflection to work out what's going on. – Boris the Spider – 2016-05-20T07:46:04.4002In C or asm (where polymorphic data types have to be implemented manually), I'm imagining accepting the multi-dimensional array in serialized form. i.e. treat the problem as a text-processing problem, just removing the internal unquoted
[
and]
characters :P – Peter Cordes – 2016-05-22T18:04:54.9832If your chosen language can't handle nested arrays, why are you worrying about using it for this challenge? It obviously can't do this challenge, which is fine. On a side note, using
Object[]
in Java andstd::any[]
in C++17 (or the equivalentboost::any
in earlier C++ versions) would work. – Mego – 2016-05-25T05:25:16.3701What if the language automatically flattens the input? – caird coinheringaahing – 2017-03-23T14:58:57.407
3Requiring support for both numbers and strings just makes this challenge unnecessarily complicated, and deviates from the core task. – Erik the Outgolfer – 2017-11-19T12:57:25.647
A test case like
["1",[2]]
would probably be good. – Giuseppe – 2018-03-29T15:09:31.543