# Flatten to a vector

**URL:** <https://discourse.julialang.org/t/flatten-to-a-vector/30408>\
**Category:** General Usage\
**Tags:** question, array, data\_structures\
**Created:** [October 28, 2019, 4:42pm UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408 "2019-10-28T16:42:15Z")\
**Posts on this page:** 7\
**Page:** 2

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 17, 2023, 11:02am UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/21 "2023-02-17T11:02:56Z")

</div>

Yeah, by default it stops at any `Real` and recurses over everything else.

The idea is you customise the `use` and `ignore` arguments to match your objects. These can be `Union` types if you need something complicated. Anything in `use` is taken as-is, anything in `ignore` is not recursed at all. Everything else is flattened until a `use` or `ignore` type is hit or there are no more child objects.

See: [Flatten.jl/Flatten.jl at master · rafaqz/Flatten.jl · GitHub](https://github.com/rafaqz/Flatten.jl/blob/master/src/Flatten.jl#L23-L24)

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [February 18, 2023, 4:55pm UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/23 "2023-02-18T16:55:32Z")

</div>

Tanks!  
I tried following the code in this simple case,

```julia
using Flatten
struct Foo{A,B,C}
           a::A
           b::B
           c::C
       end

nested = Foo(Foo(1, 2, 3), 4.0, 5.0)
#Foo{Foo{Int64,Int64,Int64},Float64,Float64}(Foo{Int64,Int64,Int64}(1, 2, 3), 4.0, 5.0)
flatten(nested)
#(1, 2, 3, 4.0, 5.0)

```

using the debugger in vscode, but when executing the function` _flatten(x, ft, use, ignore) #line 132` everything crashes with the following error

```julia
┌ Error: Some Julia code in the VS Code extension crashed
└ @ VSCodeDebugger c:\Users\sprmn\.vscode\extensions\julialang.language-julia-1.38.2\scripts\error_handler.jl:15
ERROR: Method is @generated; try `code_lowered` instead.
Stacktrace:
 [1] error(s::String)
...

```

cursor stops at line 110 `flatten_builder(T, fname) = quote`  
I assume the sequence is:

```julia
_flatten(x, ft, use, ignore)

#then
@generated _flatten(obj, flattentrait, use, ignore) = flatten_inner(obj)

#then
flatten_inner(T) = nested(T, flatten_builder, flatten_combiner)

#then
flatten_builder(T, fname) = quote
    if flattentrait($T, Val{$(QuoteNode(fname))})
        flatten(getfield(obj, $(QuoteNode(fname))), flattentrait, use, ignore)
    else
        ()
    end
end

```

but i’m not sure i understand, because the code is too complex for me, especially for the parts where metaprogramming is used.

Would it help me to understand something more if you could explain in a simple way, referring to the tested example, what function they have and what values they assume, the flattentrait, flatten\_builder and flatten\_combiner variables?

If it was this expression that causes problems for the debugger, how can I replace it with another equivalent but digestible by the debugger?

```julia
@generated _flatten(obj, flattentrait, use, ignore) = flatten_inner(obj)

```

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 18, 2023, 5:41pm UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/24 "2023-02-18T17:41:06Z")

</div>

> but i’m not sure i understand, because the code is too complex for me, especially for the parts where metaprogramming is used.

You may be in for a rough time

Recursive functions are hard to understand, `@generated` functions are hard to understand and Flatten.jl is built on recursive generated functions.

There are also some complications because traits can be used to drop out fields with e.g FieldMetadata.jl. Wich is all macros that generate macros.

I would not write something like this any more. We had a cleaner version in Accessors.jl but can’t get it type stable.

Probably the debugger cant get through tany of this, you will just have to read it and puzzle.

Try to ignore `reconstruct` thats even worse.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 18, 2023, 6:00pm UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/25 "2023-02-18T18:00:51Z")

</div>

But to answer your question a little the `flatten_builder` is building a quoted expression - its a functions that makes code. Here it needs the fiedname in a Val wrapper for the `flattenable` field trait. So it can all compile away.

`nested` is the generic recursive generated function walker all the other functions use.

`flattentrait` is any function like a FieldMetadata.jl trait that takes an object and fieldname, and returns a Bool.

The names are terrible, I apologise. It was my first year writing julia.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [February 18, 2023, 7:09pm UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/26 "2023-02-18T19:09:27Z")

</div>

but does flatten only work for nested structures of the same type?

I tried to make my own toy function to experiment with (simple) recursive functions 😀

```julia
struct B
    x1::UInt8
    x2::UInt8
    x3::UInt8
    x4::UInt8
end

x = Tuple(B(rand(UInt8, 4)...) for _ in 1:4)

struct C
    y1::B
    y2::B
end

y=[C(x[1],x[3]), C(x[2],x[4])]  

function flat(x, f=[])
    x=!isa(x,Array) ? [x] : x
    ln=mapreduce(n -> [getfield(x[n], m) for m in 1:nfields(x[n])] , vcat, 1:length(x))
    for e in ln
        eltype(e) <: Real ? push!(f,e) : flat(e,f)
    end
    f
end

flat(y)

struct Doo{X,Y,Z}
    z1::X
    z2::Y
    z3::Z
end

nested = Doo(Doo(1, 2, 3), 4.0, 5.0)

flat(nested)

using Flatten

flat(nested)
[flatten(nested)...]

 flat(y) # 6-element Vector{Any}: 0x6, 0xc9, ...
 
[flatten(y,Int)...] # Any[]

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [February 19, 2023, 10:14am UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/27 "2023-02-19T10:14:44Z")

</div>

This seems to work better, but I don’t know how general it is

```julia
struct B1
    x1::Rational
    x2::Rational
    x3::Rational
    x4::Rational
end

x = Tuple([B1([Rational(rand(-20:20,2)...) for _ in 1:4]...) for _ in 1:4])

struct C1
    y1::B1
    y2::B1
end

function flatt(x, f=[])
    m=x-> hasmethod(iterate, Tuple{typeof(x)}) ? x : [getfield(x, i) for i in 1:nfields(x)]
    for e in m(x)
        eltype(e) <: Real && length(e)==1 ? push!(f,e) : flatt(e,f)
    end
    f
end

y=[C1(x[1],x[3]), C1(x[2],x[4])]
y2=[[2//3,y], [7//8]]

flatt(y2)

y3=[[[2//3,y], 7//8, (1.0, [1,2,(3,4)])]]

flatt(y3)

```

```julia

y4=[[[2//3,y], 7//8, 'a', (1.0, [1,2,(3,4, -2+1.0*im)]),"abc",[3+2*im]]]

function flatt1(x, f=[])
    m=x-> hasmethod(iterate, Tuple{typeof(x)}) ? x : [getfield(x, i) for i in 1:nfields(x)]
    foreach(e->isbitstype(typeof(e)) && length(e)==1 ? push!(f,e) : flatt1(e,f), m(x))
    f
end

flatt1(y4)

```

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 19, 2023, 11:55am UTC](https://discourse.julialang.org/t/flatten-to-a-vector/30408/28 "2023-02-19T11:55:41Z")

</div>

> but does flatten only work for nested structures of the same type?

Flatten works with any julia objects. For example, ModelParameters.jl uses it to get parameters from any arbitrary user defined “model”.

You can do it easily without `@generated`, but it will be slow - the compiler wont follow types through the recursion, it will just give up. That’s why we generate the code - to force the compiler to compile it all away for us.

[Previous page](https://discourse.julialang.org/t/flatten-to-a-vector/30408.md?page=1)
