Merge (append) dentro do loop

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:

  1. Split the contacts array into individual items
  2. Apply a different mapping depending on the contact type (e.g. phone numbers need to be parsed and reformatted, emails pass through directly)
  3. Re-aggregate all mapped contacts back into a single array
  4. 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: