Edilitics | Data to Decisions

Flatten

Expand nested JSON, struct, and list columns into flat columns. Explode a list into rows, split it into columns, or just count its items.

Flatten expands nested columns into separate flat columns. Struct, Object, and JSON-string columns are unnested into individual fields. List columns can be exploded into one row per element, split into a fixed number of indexed columns, or reduced to an item count, depending on the mode you choose. New columns are named using the pattern originalcolumn_fieldname. The source column can be kept or dropped. You can flatten all eligible columns at once or select specific columns.

Only columns with Object, List, or Struct data types appear in the Flatten operation. String, numerical, and datetime columns are not shown. If no eligible columns exist, an info toast appears and the operation closes automatically.


When to Use Flatten

  • Unpack JSON metadata. Expand a metadata JSON column into separate metadata_source, metadata_channel columns for filtering.
  • Explode a list into rows. Convert an array-valued column into one row per value for counting or joining, when you genuinely need row-per-element granularity.
  • Split a list into columns. Turn a fixed-shape list, like a top-3 ranking or a set of tags with a known maximum count, into indexed columns on the same row instead of multiplying rows.
  • Count list items without exploding. Get "number of tags" or "number of line items" as a new integer column, with no row multiplication.
  • Prepare for Group By. Flatten nested struct fields before applying Group By on extracted sub-fields.
  • Normalize API response data. Expand deeply nested API payload columns into queryable flat fields.
  • Fix Group By warning. Group By warns when Object or List columns exist. Flatten them first to avoid data loss in aggregations.

Sample Dataset

The examples in this doc use the Edilitics sample orders dataset. Download it to follow along in your own workspace.

edilitics_sample_orders.csv

Sample orders dataset for hands-on practice · 500 rows

Download

Relevant columns for Flatten examples:

Prop

Type


List Modes

A List column supports three modes, chosen per column. Struct, Object, and JSON-string columns always unnest the same way, so no mode selector appears for them; only Max Depth applies.

Each element in the list becomes its own row. This multiplies row count: a 500-row dataset with an average list length of 3 produces approximately 1,500 rows after exploding.

Use this when you genuinely need one row per list element, such as splitting an order into one row per line item.

order_idtags
ORD-1["sale", "priority"]
ORD-2["standard"]

After exploding tags:

order_idtags
ORD-1sale
ORD-1priority
ORD-2standard

The list's elements become a fixed number of new indexed columns (tags_1, tags_2, ...) on the same row. Row count is unchanged.

You set the number of columns to create. A row with more elements than that number is rejected, not silently truncated, so no data is ever lost without your knowledge. If your data has longer lists than expected, increase the column count or switch to explode mode instead.

order_idtags
ORD-1["sale", "priority"]
ORD-2["standard"]

After splitting tags into 3 columns:

order_idtags_1tags_2tags_3
ORD-1salepriority(empty)
ORD-2standard(empty)(empty)

The list column is replaced with a single integer column holding its element count. No new columns beyond that one, no row multiplication.

order_idtags
ORD-1["sale", "priority"]
ORD-2["standard"]

After counting tags:

order_idtags_count
ORD-12
ORD-21

How to Apply Flatten

Open the Flatten operation

In your Transform pipeline, click Add Operation and select Flatten from the operation list.

If no Object, List, or Struct columns exist, an info toast appears: "No columns with objects and lists found!" and the operation closes automatically.

Choose column selection mode

By default, Select All Columns is checked. All eligible nested columns are pre-loaded into the list with default settings.

Uncheck Select All Columns to switch to custom mode. In custom mode, start with one blank row and add more using Add Column.

Review or configure columns

For a Struct, Object, or JSON-string column:

FieldDefaultDescription
Flatten Column(all eligible columns in all-columns mode)The nested column to flatten.
Max Depth3How many levels of nesting to recurse into. Options: 1, 2, 3, 4, 5.
Drop Original ColumnOn (toggled)When on, the source column is removed after flattening.

For a List column, Max Depth is replaced with a mode selector:

FieldDefaultDescription
Flatten Column(all eligible columns in all-columns mode)The nested column to flatten.
ModeExplode into rowsExplode into rows, split into columns, or count items. See List Modes above.
Number of columns5Only shown in Split into columns mode. Sets how many indexed columns to create.
Drop Original ColumnOn (toggled)When on, the source column is removed after flattening.

A warning line appears under a List row set to explode: "This multiplies row count: one new row per item in the list." A row set to Split into columns shows: "A row with more items than this will be rejected, not truncated."

Exclude columns (all-columns mode only)

In all-columns mode, click the remove icon next to any column to exclude it from flattening. Click the undo icon to re-include it. At least one column must remain included.

Add more columns (custom mode only)

Click Add Column to add another row. The button is disabled until the current row has a column selected and a valid configuration.

Click Save & Preview

Click Save & Preview. Edilitics flattens the selected columns and adds the new expanded columns to the dataset. The success toast confirms: "Flattening applied. Selected nested fields were expanded successfully."

Verify in the preview

Check the new columns. Struct fields appear as originalcolumn_fieldname. For List columns, check whether the mode you chose matches what you expected: explode increases row count, columns and count modes do not.


Before and After

Flatten metadata (Object column) with Max Depth 2, Drop Original Column on.

Input:

order_idrevenuemetadata
ORD-2024-000112450.00{"source": "web", "channel": "organic"}
ORD-2024-00478320.50{"source": "mobile", "channel": "paid"}
ORD-2024-009983841.00{"source": "web", "channel": "direct"}

Output:

order_idrevenuemetadata_sourcemetadata_channel
ORD-2024-000112450.00weborganic
ORD-2024-00478320.50mobilepaid
ORD-2024-009983841.00webdirect

Original metadata column dropped. Two new flat columns added. Row count unchanged.


How Flatten Works by Column Type

Column typeWhat happens
StructUnnested using Polars unnest. Each field becomes a new column named originalcolumn_fieldname.
Object / JSON stringParsed as JSON, converted to Struct, then unnested the same way. A value that fails to parse becomes null in the output rather than failing the whole column.
ListDepends on mode: exploded into new rows (default), split into a fixed number of indexed columns, or reduced to an item count.

Flattening a List column in explode mode multiplies rows: one row per list element. A dataset with 500 rows where the list column averages 3 elements per row will produce approximately 1,500 rows after exploding. Use Split into columns or Count items instead if you want to keep the original row count. Check row count in the preview either way.


Max Depth

Max Depth applies to Struct, Object, and JSON-string columns. It controls how many levels of nesting the operation recurses into. Default is 3.

Max DepthEffect
1Only the top-level fields of the struct are extracted. Nested structs within those fields remain nested.
3 (default)Recurses into 3 levels of nesting. Covers most real-world JSON payloads.
5Maximum in the UI. Use for deeply nested API responses or complex Parquet schemas.

New column names at each level append the field name: metadata_user_address_city at depth 3.


Code Equivalent

-- PostgreSQL: expand JSON object column
SELECT
  order_id,
  revenue,
  metadata->>'source' AS metadata_source,
  metadata->>'channel' AS metadata_channel
FROM orders;

-- BigQuery: flatten STRUCT column
SELECT
  order_id,
  revenue,
  metadata.source AS metadata_source,
  metadata.channel AS metadata_channel
FROM orders;
import polars as pl

# Flatten struct/JSON column (dtype must be supplied explicitly)
df = df.with_columns(
    pl.col("metadata").str.json_decode(pl.Struct({"source": pl.String, "channel": pl.String}))
).unnest("metadata").rename({
    "source": "metadata_source",
    "channel": "metadata_channel"
})

# Explode list column into rows
df = df.explode("tags")

# Split list column into indexed columns instead of exploding
df = df.with_columns(
    pl.col("tags").list.to_struct(fields=["tags_1", "tags_2", "tags_3"])
).unnest("tags")

# Count list items without exploding
df = df.with_columns(pl.col("tags").list.len().alias("tags_count"))

After Save & Preview, the pipeline shows a DQ delta badge on this step: green if the table score improved, red if it dropped. See Data Quality Scoring for how scores are calculated.


Operation Reference

Prop

Type


Frequently Asked Questions


Next Steps

Need help? Email support@edilitics.com with your workspace, job ID, and context. We reply within one business day.

Last updated on

On this page