n8n Data Sort, Merge, Split & Group By Workflow Operations

I sort, merge, split, and group data in n8n because raw data from APIs and databases is almost never organized the way you need it. Sorting puts items in order. Merging combines related data. Splitting breaks apart complex structures. Grouping organizes by category. Here’s how I handle each operation.

Data preparation is the most overlooked part of workflow automation. Everyone focuses on connecting systems. Nobody talks about organizing the data flowing between them. I used to skip this step and wonder why my spreadsheets looked wrong or my database inserts failed. Once I added proper sort, merge, split, and group operations, everything became reliable.

Sorting Data in n8n

Sorting arranges items in a specific order. n8n doesn’t have a dedicated Sort node, so I use the Code node with JavaScript’s sort method. It’s straightforward and gives you full control.

For ascending alphabetical sort:

`javascript
// Sort: Alphabetical ascending
const items = $input.all();
items.sort((a, b) => {
const nameA = a.json.name?.toLowerCase() || ”;
const nameB = b.json.name?.toLowerCase() || ”;
return nameA.localeCompare(nameB);
});
return items;
`

For numeric sort by a specific field:

`javascript
// Sort: Numeric descending by amount
const items = $input.all();
items.sort((a, b) => (b.json.amount || 0) – (a.json.amount || 0));
return items;
`

For date sorting, convert to timestamps for comparison:

`javascript
// Sort: Chronological by date field
const items = $input.all();
items.sort((a, b) => {
const dateA = new Date(a.json.created_at || ‘1970-01-01’).getTime();
const dateB = new Date(b.json.created_at || ‘1970-01-01’).getTime();
return dateA – dateB;
});
return items;
`

Multi-field sorting is common when you need secondary sort keys. Sort by department first, then by salary within each department:

`javascript
// Sort: Primary by department, secondary by salary descending
const items = $input.all();
items.sort((a, b) => {
const deptCompare = (a.json.department || ”).localeCompare(b.json.department || ”);
if (deptCompare !== 0) return deptCompare;
return (b.json.salary || 0) – (a.json.salary || 0);
});
return items;
`

The Set node can sort simple lists. If you just need to reorder a flat array, use the Expression node with array functions. But for field-based sorting on complex objects, the Code node is the way to go.

Merging Data from Multiple Sources

Merging combines data from different sources into a unified dataset. The Merge node in n8n handles this visually. You connect two inputs, specify the join type, and define the matching key.

For inner joins (only matching records):

`
Input A: User records from database
Input B: Order history from API
Join Key: user_id
Join Type: Inner Join
`

Only users with order history appear in the output. Users without orders are excluded. This is useful for targeted campaigns where you only want to reach customers who have purchased before.

For outer joins (all records from both sides):

`
Input A: Product catalog
Input B: Inventory levels
Join Key: product_id
Join Type: Full Outer Join
`

All products appear, whether or not inventory data exists. Missing inventory shows as null. You can then decide how to handle those gaps — skip them, use default values, or flag them for review.

For merging arrays within a single item, use the Code node:

`javascript
// Merge: Combine two arrays from same item
const primary = $input.all()[0].json.primary_list || [];
const secondary = $input.all()[0].json.secondary_list || [];

// Deduplicate by ID
const mergedMap = new Map();
primary.concat(secondary).forEach(item => {
mergedMap.set(item.id, item);
});

return [{
json: {
…$input.all()[0].json,
merged_list: Array.from(mergedMap.values())
}
}];
`

This merges two arrays, removes duplicates by ID, and preserves the most recent version of each item. Useful when data comes from multiple sources with potential overlap.

Splitting Data into Chunks

Splitting breaks large datasets into smaller pieces. This is essential when dealing with API rate limits or batch processing constraints. If an API accepts 100 items per request and you have 500 items, you need to split.

The Code node handles chunking cleanly:

`javascript
// Split: Chunk array into batches of N
const batchSize = 50;
const items = $input.all();
const chunks = [];

for (let i = 0; i < items.length; i += batchSize) { chunks.push(items.slice(i, i + batchSize)); }

// Return each chunk as a separate item
return chunks.map(chunk => ({
json: {
batch_number: chunks.indexOf(chunk) + 1,
total_batches: chunks.length,
items: chunk.map(c => c.json)
}
}));
`

This splits items into batches of 50. Each output item contains a batch_number, total_batches count, and the items in that batch. Downstream nodes can process each batch independently.

For splitting a single item’s data into multiple items:

`javascript
// Split: One item becomes many
const item = $input.all()[0].json;
const results = [];

item.questions.forEach(q => {
results.push({
json: {
survey_id: item.id,
question: q.text,
category: q.category,
priority: q.priority
}
});
});

return results;
`

A single survey response containing multiple questions becomes multiple items — one per question. Each downstream operation processes individual questions instead of the entire survey at once.

Grouping Data by Fields

Grouping organizes items into buckets based on a shared property. The n8n Merge node can group when combined with database queries, but the Code node offers more flexibility.

Simple grouping by a single field:

`javascript
// Group: Organize by department
const items = $input.all().map(item => item.json);
const groups = {};

items.forEach(item => {
const dept = item.department || ‘unassigned’;
if (!groups[dept]) groups[dept] = [];
groups[dept].push(item);
});

// Output one item per group
return Object.entries(groups).map(([name, members]) => ({
json: {
department: name,
member_count: members.length,
members: members
}
}));
`

This groups employees by department and outputs one item per department with the full member list included. Perfect for generating department-level reports.

Nested grouping handles multiple levels:

`javascript
// Group: Department then role
const items = $input.all().map(item => item.json);
const groups = {};

items.forEach(item => {
const dept = item.department || ‘unassigned’;
const role = item.role || ‘unknown’;
if (!groups[dept]) groups[dept] = {};
if (!groups[dept][role]) groups[dept][role] = [];
groups[dept][role].push(item);
});

// Flatten to output format
const results = [];
Object.entries(groups).forEach(([dept, roles]) => {
Object.entries(roles).forEach(([role, members]) => {
results.push({
json: {
department: dept,
role: role,
count: members.length,
members: members
}
});
});
});

return results;
`

Department and role combinations become distinct groups. Each output row represents one department-role pair with its members. This is the kind of grouping that would take multiple SQL GROUP BY clauses.

Combining Operations

Real-world data rarely needs just one operation. I typically chain sort, filter, merge, and group in sequence:

`
1. Sort by date (newest first)
2. Filter by status (active only)
3. Merge with lookup data
4. Group by category
5. Sort groups by count (largest first)
`

Each step transforms the data incrementally. The final output is clean, organized, and ready for its destination. The n8n Data Transform guide covers the transformation patterns that feed into these operations.

For database insert operations, grouped and sorted data reduces the number of queries. Instead of inserting 500 rows one at a time, you insert 10 grouped batches. Faster and less resource-intensive.

Action Card: Quick Operation Cheatsheet

Common patterns for each operation:

`javascript
// SORT: Sort by any field
$input.all().sort((a,b) => (a.json.field||”).localeCompare(b.json.field||”));

// MERGE: Combine two arrays
[…arr1, …arr2].filter((v,i,a) => a.findIndex(x=>x.id===v.id)===i);

// SPLIT: Chunk into batches
const n=100; return Array.from({length:Math.ceil(len/n)},(_,i)=>items.slice(in,in+n));

// GROUP: By single field
const g={}; items.forEach(i=>(g[i.key]=g[i.key]||[]).push(i));
`

Chain these operations in sequence for complex data preparation pipelines. Each operation feeds into the next.

References

  • n8n Data Transform Patterns
  • n8n Database Nodes
  • n8n Core Nodes
  • n8n Code Node Guide
  • JavaScript Array.sort()
  • Leave a Reply

    Your email address will not be published. Required fields are marked *