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
Bem-vindo @thomaslc à nossa comunidade! Sou Jay e sou um criador verificado da n8n.
O nó Merge (combinar por posição) dentro de um loop SplitInBatches quebra porque espera que duas entradas cheguem simultaneamente - mas dentro de um loop, apenas uma ramificação está ativa por iteração. Não foi projetado para esse padrão.
A solução mais limpa aqui é pular o loop interno + Merge inteiramente e lidar com a transformação completa de contatos em um único nó Code por cliente. Algo assim:
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 } }];
Isso é executado por item no seu loop externo, transforma o array de contatos no local e alimenta direto seu payload final - nenhum loop interno ou Merge necessário.
Limitação interessante. Você poderia simplificar o fluxo de trabalho usando um nó Code (conforme respondido anteriormente acima) ou até mesmo um nó Edit Fields com um pouco de JS. Outra opção é passar a funcionalidade de loop para um sub-fluxo de trabalho e mesclar a saída do sub-fluxo de trabalho.
A forma mais limpa de lidar com isso é um único nó Code que executa todos os quatro passos de uma vez, pulando o loop inteiramente:
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 } };
});
Isso evita o conflito loop-merge porque você está transformando o array inline. O Merge (combine by position) quebrando o loop é esperado - os loops do n8n não lidam bem com agregação no meio do loop. Se você precisar manter a abordagem baseada em nós, passe cada cliente processado para um sub-workflow via Execute Workflow e agregue os resultados após todas as execuções serem concluídas.
Olá @nguyenthieutoan, @njogued
Obrigado pelo feedback! O motivo de eu usar um loop é que utilizo nós customizados como um nó verificador de telefone. Um nó de código não conseguiria substituir isso.
O que acabei fazendo foi criar um subfluxo de trabalho que será chamado usando a opção “Executar uma vez para cada item”.
oi @thomaslc , bem vindo!
eu olhei na documentação em relação a esses itens, e no meu entendimento isso tem a ver com a forma que o n8n mantém o contexto do item. quando você usa Split Out, Loop Over Items, Merge e Aggregate, ele precisa preservar ou reconstruir o vínculo entre cada contato agregado e o customer original. O Merge combine by position pode quebrar esse raciocínio se a quantidade ou ordem dos itens não for exatamente a mesma.
segue os docs que vão te ajudar: