How to repeat action(s) in your Zap for a variable number of values

How to repeat action(s) in your Zap for a variable number of values
Userlevel 7
Badge +3
  • Zapier Staff
  • 21 replies

Last Updated: 05-27-2021

Update

There is now a 3rd option for accomplishing this: Looping by Zapier!

Looping by Zapier was built with some of the issues in mind that could crop up using a Code Step, like not inputting Line Item data with null values properly. It works similarly to the Code Step below behind the scenes, but has some nice bells and whistles, like Line Item support and more.

You can read about it here: https://zapier.com/help/create/other-functions/loop-your-zap-actions

You can add it to your Zaps by searching for Looping by Zapier as the app when adding a new Step.

Intro

Sometimes we want to run an Action or set of Actions more than once in our Zaps. Today I’d like to show you how we can do that if the number of times we want to run that action is variable. 

In the world of programming, we can create loops that run a bit of code once for each of a set of values. We can do this in a Zap as well. If you don’t know how to code, that’s ok! It’s my hope that with this guide, you won’t need to know how to take advantage of this.

Let’s say I have a Trigger that provides a Line Item (array) or comma separated text containing email addresses and first names and I’d like to send a customized email to each one of them:

If we tried to map the email addresses into the “To:” field, that line item would be converted into comma separated text, so a single email would be sent to all the recipients, but they would all see each other’s email address. Worse, the names would be a comma separated text list in the greeting of our email.

What we really want is for the Zap to run the email Step once for each email and name so that each recipient gets a separate email. 

The ideas that we’ll explore would also apply to circumstances where we want to add multiple contacts or leads to a CRM, create multiple documents, send multiple text messages, and more. If an Action supports line items, that support can be used many times for a similar effect, like adding multiple rows to a spreadsheet in Google Sheet’s “Create Spreadsheet Row(s)”. Many actions don’t support line items, and that’s where these techniques come in handy. 

There are two ways to create a loop to repeat one or more actions for different values:

Option 1: Use Google Sheets as an intermediary

This method involves setting up two Zaps and a Google Sheet:

Google Sheet

  • Create a header for each value that you want to repeat in one or more actions

Zap 1

  • Your original Trigger
  • Actions before you want to loop
  • If the values aren’t already in Line Item format, use the Formatter App’s Utility Action: Line Itemizer transform to convert the values into line items (unless they already are)
  • Action: Google Sheets: Create Spreadsheet Row(s) (Mapping each value you want to send to  into the various fields in the sheet - 1 or more rows will be created for the line items. You can mix and match single values and line item values. Line item values each be used once per row. Single values will be repeated for each row - think values like name or email address)

Zap 2 (runs once for each row created by the line items in Zap 1): 

  • Trigger: Google Sheets: New Spreadsheet Row
  • Action: One or more Action(s) that you want to repeat 

In summary, Zap 1 “sets up” the loop, and Zap 2 runs the looped actions with the values posted to each row of the sheet.

Option 2: Use a “Code by Zapier” Action (sometimes referred to as a “Code Step”) to “fork” a Zap

Option 1 works well enough, but it is more difficult to manage over time than a single Zap because we have two Zaps and a spreadsheet to keep track of. Let’s take a look at how using a small amount of reusable code, we can keep things in a single Zap.

Most Steps in Zapier give an output of one JSON object wrapped in an array. When using any value from that object in a subsequent Step, that Step will run once. If we use a Code Step to output multiple JSON objects of the same structure wrapped in an array, the later Steps will run once for each object in the array instead, creating the loop we’re looking for. 

A common example of this is that you're already familiar with is a Zap Trigger. Triggers that ask an app for new data to Trigger on get 0 or more objects in an array returned, and then the Zap runs once for each object. 

​When we’re testing this in the Zap editor, similar to how we can only use one Sample from a Trigger at a time, we’ll only see the first object, and the Zap Steps will only run once when testing. When running live, they will run once per object (set of values).

First, let’s take a look at the values we’re going to input into the Code Step. They could be Line Items like this:

Or comma separated text like this:

When we map the values into the Code Step, they will be converted to comma separated text if they’re not already, so the both examples above will work the same way.

When you’re adding the Code Step to your Zap, you’ll specifically want to use “Run Javascript” if you’re using the code I’ve included here.

When we map those values into the Code Step, we’ll want to types names for each set of values on the left of the Input Data fields (green highlight) and map the actual series of values on the right (purple):

You can paste the following Code into the Code Field as-is. You won’t need to modify it all, regardless of the names and quantities of different values you’re using. It will detect the names of your values automatically from the Input Data field (green highlight). Here’s the code to copy and paste:

/* Add as many Input Data fields as you like above as comma seperated text or mapped line items (will be 
converted to comma seperated text). This code will find each Input Data field and output an array of
objects with the same structure that can be used to "Fork" the Zap.
Example: https://cdn.zappy.app/9de81901f3750ef26bcbbd0737b0937b.png */

// get Input Data field names
let keys = Object.keys(inputData)
let data = [];

// loop through each Input Data field
for (let key of keys) {
// split the contents of each Input Data field on the commas into an array
let li = inputData[key].split(",");
for (let i=0; i<li.length; i++) {
if (typeof data[i] === "undefined") data[i] = {};
data[i][key] = li[i];
// add a record number (in case we want to break the fork/loop with a Filter)
data[i].recordNumber = i+1;
}
}

// preview the whole data structure in the output
console.log(data);
// output the data
output = data;

When you test the Code Step, you’ll see something like this:

As you can see, the the values that we get back (in red) match the names of the Input Data fields and additionally have a “recordNumber” value, but we only get the first set of values to test with, just like when we’re testing a Trigger. By mapping any of those values into a field in the next Action, when the Zap runs live, that Action will be repeated for each set of values. 

In order to give you a better preview of what will actually happen with the live Zap, I’ve set the code to output a “log” that shows all the sets of value (highlighted in green). You can see how the first record is listed there, too (in red).

Here’s what it looks like if we map those values into an Email by Zapier action:


​Once the Zap starts looping, every Action from that point on will loop and run multiple times. If we need to break the loop, we can do that by:

  • Where we want the loop to stop, add a Filter that only continues the Zap if the “Record Number” value exactly matches to “1”. 
  • The Filter will loop, too, but will only pass once for the first set of values in the loop, meaning that we’ll be back to not looping for the Actions after that. 

If we don’t need to run any Actions just once after the ones that we want to loop, we don’t need to worry about breaking the loop.


And that should do it! With these workflows, Zap Actions that can only send a single set of values to an app can send multiple sets of values instead!

If you have any questions about this, I’d be happy to field them here!

Please note

We aren't always able to help with Code questions via Zapier Support because not everyone on the Support Team is familiar with Javascript and Python, and supporting custom code is technically outside of our scope of support. A great place to ask if we can't support you directly is here in our community or on Stack Overflow by tagging your question "Zapier": https://stackoverflow.com/questions/tagged/zapier

 


This post has been closed for comments. Please create a new post if you need help or have a question about this topic.

39 replies

Userlevel 1
Badge

RicoSystems,

What I got   (the difference in they input data was that only the Quantity has commas without data in between.0

 

Userlevel 3
Badge +1

Chris,

One of the little quirks of Zapier and object and arrays is that in some cases you lose the ability to parse things correctly if they are not communicated as JSON strings. Nulls and string values with internal commas can be particularly difficult.

To that end, I think you’ll have better luck if you use the code I sent you most recently, applying the JSON.stringify fix to create an array of JSON strings. That will still trigger the fork, but each fork will get a single JSON string which can be parsed into the line item object you need. If you go back up to your original inputs - the three or four arrays that you concatenate, then you’ll be on track. If that fork is driven by an array of JSON strings, then the subsequent fork structure can operate the same way, with a filter step to separate the first fork from the subsequent fork(s).

Bear in mind the overall limitations that Tim mentioned.

If you’d find it helpful to continue this either via email or chat, just Google me - I’m easy to find.

Joey (aka RisoSystems)

Userlevel 3
Badge +1

Chris,

PS: Remember that a string is treated like an array - you have to parse the thing back to an array of objects before an index will work the way you need.

ie:

If StringValue = ‘ “none”, “something”, “something else”, “something, or maybe nothing” ‘ then StringValue[14] == “h”

If StringArrayValue = [“none”, “something”, “something else”, “something, or maybe nothing”] then StringArrayValue[2] == “something else”

 

 

Hi @jonah and @ForYourIT,

Thanks for the questions and answers here! @ForYourIT is correct that we need to keep those limitations in mind, though in this case the answer is likely something else. 

I consulted with our Support Escalations team and I learned that the maximum number of times we can loop/fork in a Zap is 250. With 732 values to loop through, we exceed that limit and the Zap won’t work. 

I don’t believe that number can be changed, but I’m looking into learning more about this, and if there is a good workaround/solution, I’ll post another reply.

Hello! Is there any update on this particular limitation? We’re running into the same problem.

My action is a webhook GET that returns an array of data that I cannot filter from this particular service (Paychex) due to an API limitation, so it is returning over 250 results in the JSON. It does, however, allow for pagination, but I’m not sure how to tackle that using Zapier.

Any ideas of a workaround to get the Javascript code to either work for >250 items OR alternatively, limit the number of responses using API pagination?

Any help is greatly appreciated!

Userlevel 3
Badge +1

Tim, if the array that kicks off the loop were parsed in such a way that groups of array items would Branch off to other zaps would the same limitation still apply? for example, could the first 100 values of the array the bundled up and sent to one's app while another hundred gets sent in a separate batch? Exactly how does the limitation apply?

Userlevel 1

Just wanted to chime in and say this saved my day (and a ton of my time in the future)!  Thanks for sharing!

Userlevel 1

Hey Tim, 

Thanks for this solution! I tried the code out and wanted to ask if there’s a way for the first line “Deal” to repeat? So that “Order 1” would be repeated for each “Product”? 

Right now, the zap doesn’t repeat the first line “deal”: 

 

Ideally, it would look like this: 

 

Any help is greatly appreciated! Thank you!

Userlevel 1
Badge

@TimS is it possible to nest multiple loops? i.e. code step>using line items in step>another code step>using those line items?

Userlevel 1

@TimS You are an absolute godsend.

 

Not only have you been able to the solution to solve a very specific problem but you have provided it in a way that can be reused to solve any number of other problems using only the Zapier inputs!

 

Seriously man, great work, the world needs more people capable of providing solutions like this one, if you are ever in New Zealand I owe you a drink of your choosing.

Userlevel 1

@TimS - Can you please help me out with using this code?  I’m trying to understand how to deal with blank values coming in to this step from a CSV.

 

Background:  I’m running a daily download from a wordpress website.  I then need to parse the data and pass it to a ticket in Hubspot.

 

It appears that the data is being read correctly from the CSV. 

 

The input data where I am getting blanks is the field RFP Toolkit. You can see that there are blanks in the CSV file.

 

I have not altered your original code.

 

The output log shows the 1 value being assigned to record 1, not to record 3.  And records 2, 3, 4 are blank.

 

I have also looked at your post about assigning default values to Code steps, but that resulted in the same output, with the 1 being assigned to the first record.

 

Any insight you could offer would be incredibly helpful.

 

Scott

Userlevel 7
Badge +3

@TimS is it possible to nest multiple loops? i.e. code step>using line items in step>another code step>using those line items?

Hey @David@Zapwise ! 
It is technically possible but not advisable. We discovered while working on the Looping by Zapier app that nested looped actions often get stuck in a playing state, so we prevent Zaps from turning on with multiple Looping Steps. That being said, we’re working on improving the way the underlying tech works for Looping so that in the future nested Looping will work.

Userlevel 7
Badge +3

I’m trying to understand how to deal with blank values coming in to this step from a CSV.


Hey @scottfmosley !

The reason that you’re getting those inconsistent results with the blank fields is that the Code by Zapier Step doesn’t have native line item fields. That means that each element of the line items from the CSV are converted into comma separated strings, and null (empty) values are dropped.

When we try to line up the different elements where some have had entries dropped, they get misaligned like that.

This is part of the reason that we created the Looping by Zapier Beta app. It has a “Create Loop from Line Items” Action that does essentially the same thing as the code here, but with proper Line Item fields so that data isn’t misaligned when there are null values, and we made the interface a bit nicer, too!

I’d definitely recommend trying the Looping by Zapier Action instead of the Code Step first.

If you find you wanted to do something custom in the code, you can read more about how to get line items into Code Steps intact in a more recent post I wrote here: 

It’s a long post, but it’s mostly screenshots because this is a tricky thing to get right!

Userlevel 1

@TimS - Worked like a charm!  Thanks very much.

Hi - everyone on this thread is amazing and everything you’ve shown is SO CLOSE to helping me solve my problem. But not quite yet.

 

This is start-to-finish the outline of my Zap so far. I have users submit forms with multiple image attachments. I need each of those images to create its own line in an AirTable database.

 

 

 

Here is the end result. The top line is what is happening; the next 4 are what I want to happen (I am fixing these manually right now.)

 



Here’s my JavaScript section:

 

This is the output I get

 

 

 

And this is what I’m doing in the AirTable section (‘Preview’ is the name of the field in AirTable where the photo attachments are stored/viewed.)