Hello n8n community!
I have searched the documentation but did not find a successful answer to my problem.
The use case
I’m processing a list of customer records. Each customer has a contacts array containing items of different types (e.g. phone, email). My goal is to:
- Split the
contacts array into individual items
- Apply a different mapping depending on the contact type (e.g. phone numbers need to be parsed and reformatted, emails pass through directly)
- Re-aggregate all mapped contacts back into a single array
- Merge that array back into the original customer item to build the final payload
The workflow
The Merge (append) node works inside a loop when the Aggregate is the last node. However, adding a Merge (combine by position) after the Aggregate — which is necessary to reassemble the contacts back into the customer item — breaks the loop entirely.
Test data:
{
"data": [{
"firstName": "John",
"lastName": "Doe",
"contacts": [
{
"type": "phone",
"value": "+33652252525"
}
]
},
{
"firstName": "Mary",
"lastName": "Doe",
"contacts": [
{
"type": "email",
"value": "marydoe@example.com"
}
]
},
{
"firstName": "John",
"lastName": "Smith",
"contacts": [
{
"type": "email",
"value": "johnsmith@example.com"
},
{
"type": "phone",
"value": "+33652252526"
}
]
}]
}
Workflow
Information on your n8n setup
- n8n version: 2.20.9
- Database (default: SQLite): PostgrSQL
- Running n8n via (Docker, npm, n8n cloud, desktop app): Docker
¡Bienvenido @thomaslc a nuestra comunidad! Soy Jay y soy un creador verificado de n8n.
El nodo Merge (combinar por posición) dentro de un bucle SplitInBatches se rompe porque espera que dos entradas lleguen simultáneamente - pero dentro de un bucle, solo una rama está activa por iteración. No está diseñado para ese patrón.
La solución más limpia aquí es omitir completamente el bucle interno + Merge y manejar la transformación completa de contactos en un único nodo Code por cliente. Algo así:
const contacts = $json.contacts;
const mapped = contacts.map(c => {
if (c.type === 'phone') {
return { type: 'phone', value: c.value.replace(/\s/g, '') };
}
return c;
});
return [{ json: { ...$json, contacts: mapped } }];
Esto se ejecuta por elemento en tu bucle externo, transforma el array de contactos en su lugar, y se alimenta directamente a tu carga útil final - sin necesidad de bucle interno o Merge.
Limitación interesante. Podrías simplificar el flujo de trabajo utilizando un nodo Code (como se respondió anteriormente) o incluso un nodo Edit Fields con algo de JS. Otra opción es pasar la funcionalidad del bucle a un sub-flujo de trabajo y fusionar la salida del sub-flujo de trabajo.
La forma más limpia de manejar esto es un único nodo Code que realiza los cuatro pasos en una sola pasada, saltándose el bucle por completo:
return items.map(item => {
const mapped = item.json.contacts.map(c => {
if (c.type === 'phone') return { ...c, value: c.value.replace(/\D/g, '') };
return c; // email passes through
});
return { json: { ...item.json, contacts: mapped } };
});
Esto evita el conflicto bucle-fusión porque estás transformando el array en línea. El Merge (combinar por posición) rompiendo el bucle es lo esperado - los bucles de n8n no manejan bien la agregación a mitad del bucle. Si necesitas mantener el enfoque basado en nodos, pasa cada cliente procesado a un sub-flujo mediante Execute Workflow y agrega los resultados después de que todas las ejecuciones se completen.
Hola @nguyenthieutoan, @njogued
¡Gracias por tu retroalimentación! La razón por la que estoy usando un loop es que uso nodos personalizados como un nodo verificador de teléfono. Un nodo de código no podría reemplazarlo.
Lo que terminé haciendo fue crear un subworkflow que será llamado usando la opción “Run once for each item”.
hola @thomaslc , ¡bienvenido!
miré la documentación en relación a esos elementos, y en mi entendimiento esto tiene que ver con la forma en que n8n mantiene el contexto del elemento. cuando usas Split Out, Loop Over Items, Merge y Aggregate, necesita preservar o reconstruir el vínculo entre cada contacto agregado y el cliente original. El Merge combine by position puede romper ese raciocinio si la cantidad u orden de los elementos no es exactamente la misma.
aquí están los docs que te van a ayudar: