Performing a merge using custom rules in Code node

Describe the problem/error/question

Our current HRIS system doesn’t integrate into Okta, so I’m trying to build a flow that periodically gets employee details to update the corresponding user in Okta.

The problem, however, is that the HRIS system uses employees’ legal name, whereas users have opted for a preferred name in Okta. For example:

  • Some users have a surname preposition like in the HRIS system, but opt to drop that in their user account (eg. Baron von Trappe | Baron Trappe)
  • Hispanic users often have multiple surnames which is present in the HRIS system, but they opt to keep just the first surname in their user account (eg. Juan Fernando Hernandez | Juan Fernando)
  • Many users have a formal legal given name, but opt for a shortened version (eg. William Smith | Will Smith)

As the Merge node only allows me to specify the field names to match, but not manipulate it with an expression to conduct the match, I want to resort to a Code node to handle the matching.

I’ve used the following code in the node:

const hrisUsers = $('GET HRIS Employees').all().map(employee => {
    return {
        json: employee.json.attributes
    };
});

const oktaUsers = $('GET Okta Users').all().map(user => {
    return {
        json: user.json.profile
    };
});

const surnamePrepositions = ['da', 'de', 'del', 'der', 'di', 'van', 'von', 'ver'];
const firstNameMap = {
    "Andrew": ["Andy"],
    "Christopher": ["Chris"],
    "David": ["Dave"],
    "Donald": ["Don"],
    "Edward": ["Ed", "Eddy"],
    "Gregory": ["Greg"],
    "Jacob": ["Jake"],
    "Jonathan": ["Jon", "John"],
    "Matthew": ["Matt"],
    "Michael": ["Mike"],
    "Peter": ["Pete"],
    "Richard": ["Rich", "Rick"],
    "Robert": ["Rob"],
    "Samuel": ["Sam"],
    "Stuart": ["Stu"],
    "Thomas": ["Tom", "Tommy"],
    "Timothy": ["Tim"],
    "William": ["Will", "Bill"],
};

function normalizeLastName(lastName) {
  if(lastName != null){
  let parts = lastName.split(' '); // Split the last name into parts
    // Filter out surname prepositions
    parts = parts.filter(part => !surnamePrepositions.includes(part.toLowerCase()));
    // Return the first remaining part, or an empty string if none
    return parts.length > 0 ? parts[0] : '';
  }
}

function formalizeFirstName(firstName) {
    for (const [key, value] of Object.entries(firstNameMap)) {
        if (value.includes(firstName)) {
            return key;
        }
    }
    return firstName;
}

// Create a new array to hold the merged users
const matchedUsers = [];
const unmatchedUsers = [];

// Iterate over hrisUsers
for (const hrisUser of hrisUsers) {
    const firstName = formalizeFirstName(hrisUser.json.first_name);
    const lastName = normalizeLastName(hrisUser.json.last_name);

    // Find matching okta user
    const matchedOktaUser = oktaUsers.find(oktaUser => 
        formalizeFirstName(oktaUser.json.firstName) === firstName && normalizeLastName(oktaUser.json.lastName) === lastName
    );

    // If a match is found, merge the user data
    if (matchedOktaUser) {
        matchedUsers.push({
            hrisData: hrisUser.json,
            oktaData: matchedOktaUser.json
        });
    } else {
        // If no match is found, push to unmatchedUsers
        unmatchedUsers.push(hrisUser.json);
    }
}

// Return the merged users
return matchedUsers;

What is the error message (if any)?

While the Code works if I am using pinned data in the GET HRIS Employees and GET Okta Users HTTP requests, it fails if I execute the workflow with those two nodes in separate branches, as the Code node executes after one of the HTTP requests completes but the other branch has not initiated, leading to the following error: Referenced node is unexecuted [line 2]

If I try to circumvent this by having both HTTP requests occur within the same branch, then the multiple items returned by the first node causes the subsequent node to run that many multiple times as a batch. Toggling Execute Once doesn’t appear to do anything.

A pointer in the right direction would be greatly appreciated, whether on getting separate branches working, or how to prevent multiple runs from being initiated by a previous node’s results.

Please share your workflow

Share the output returned by the last node

Referenced node is unexecuted [line 2]

Information on your n8n setup

  • n8n version: 1.63.4
  • Database (default: SQLite): default
  • n8n EXECUTIONS_PROCESS setting (default: own, main): default
  • Running n8n via (Docker, npm, n8n cloud, desktop app): npm
  • Operating system: Ubuntu 24.04

Hey @ThomasLu_EarthDaily, you would need to merge the two branches first with the output of Merge node connected to the input of Code node. Surely, the references to the individual HTTP Request nodes would have to be updated accordingly and in particular switched to $input.

Hi @ihortom,

Thanks for the suggestion, but I think my instance of n8n had gone haywire.

Execute Once appears to work after I updated it to the latest version, from my instance which was 3 versions behind.

As such, I’m able to just have everything run linearly without multiple items causing subsequent nodes to run multiples of times.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.