SOLVED

Conditional forEach statement in Velocity Script

Go to solution
Jon_Wright
Level 3

Conditional forEach statement in Velocity Script

I've been using Sanford Whiteman​'s excellent resource on using JSON in Velocity script but am stuck on how I'd go about only displaying certain records from the JSON.

For example in the following I only want to list the relevant values where {{lead.email domain}} = allProjects.domain. So anyone with a domain of company01.com would see the first 2 records listed, whereas company02.com would only see the last record (there will be about 50 - 100 records in the JSON eventually). Any pointers greatly appreciated!

#set( $allProjects = [

{

"domain": "Company01.com",

"category": {

"URL": "https://www.example.com/project1",

"companyName": "Company 01",

"name": "Example Project Name 01",

"participantType": "Champion"

}

},

{

"domain": "Company01.com",

"category": {

"URL": "https://www.example.com/project2",

"companyName": "Company 01",

"name": "Example Project Name 02",

"participantType": "Champion"

}

},

{

"domain": "Company02.com",

"category": {

"URL": "https://www.example.com/project1",

"companyName": "Company 02",

"name": "Example Project Name 01",

"participantType": "Champion"

}

}

}

] )

<p><br /></p>

<ul>

#foreach( ${allProjects.domain} in $allProjects )

<li>

<a href="${allProjects.category.URL}" target="_blank" id="">${allProjects.category.name}</a></li>

#end

</ul>

1 ACCEPTED SOLUTION

Accepted Solutions
SanfordWhiteman
Level 10 - Community Moderator

Re: Conditional forEach statement in Velolcity Script

There's no built-in collection filtering w/predicates in Velocity. So you do a simple loop and move the matches into another collection:

#set( $domainProjects = [] )

#foreach( $project in $allProjects )

#if( $project.domain.equalsIgnoreCase($lead.emailDomain) )

#set( $void = $domainProjects.add($project) )

#end

#end

#foreach( $project in $domainProjects )

## this is your subset of matches

#end

Your {{lead.email domain}} token, when you check it off and drag it onto the Velocity canvas, isn't going to have a space in it (the Velocity $lead.property name isn't the same as the token name). So I've used $lead.emailDomain here.

View solution in original post

12 REPLIES 12
SanfordWhiteman
Level 10 - Community Moderator

Re: Conditional forEach statement in Velolcity Script

There's no built-in collection filtering w/predicates in Velocity. So you do a simple loop and move the matches into another collection:

#set( $domainProjects = [] )

#foreach( $project in $allProjects )

#if( $project.domain.equalsIgnoreCase($lead.emailDomain) )

#set( $void = $domainProjects.add($project) )

#end

#end

#foreach( $project in $domainProjects )

## this is your subset of matches

#end

Your {{lead.email domain}} token, when you check it off and drag it onto the Velocity canvas, isn't going to have a space in it (the Velocity $lead.property name isn't the same as the token name). So I've used $lead.emailDomain here.

View solution in original post

Mark_Price
Level 7

Re: Conditional forEach statement in Velolcity Script

Once you get the #foreach working, you may then be surprised by an obscure system behavior with links and #foreach loops.

If you are going to output a link from within a #foreach loop, you will probably need to add the class "mktNoTrack" to your link OR deploy a workaround. Otherwise, you'll potentially output a dead links.

Here's a resource that helped me understand what was going on when encountering this issue: https://blog.teknkl.com/multiple-marketo-tracked-links-in-velocity/

Jon_Wright
Level 3

Re: Conditional forEach statement in Velolcity Script

Thanks for the tip, Mark! I am planning to output links so this is very helpful.

Jon_Wright
Level 3

Re: Conditional forEach statement in Velolcity Script

Ouch. That's kind of a big bug, at least for what I had planned. Is this something Marketo plan to fix?

SanfordWhiteman
Level 10 - Community Moderator

Re: Conditional forEach statement in Velolcity Script

There's no plan to fix it, but you can work around it easily (if clumsily) by preallocating up to a max # of variables, like in my blog post.

There are also some situations in which the bug surprises me by not kicking in. I went so far as to think the behavior had been changed a few months ago... but no, it was because of one exception-to-the-exception. You might as well assume it's always a problem.

Jon_Wright
Level 3

Re: Conditional forEach statement in Velolcity Script

Thanks, Sanford - I really do appreciate all of your resources on this. But, as a novice with VTL, when you have to use a giant If statement to use a forEach, I'm thinking I may as well just use a giant If statement without the forEach.

It feels like Marketo have dropped the ball on this one. Surely multiple (tracked) links from COs would be a common use case for VTL?

SanfordWhiteman
Level 10 - Community Moderator

Re: Conditional forEach statement in Velolcity Script

It's still better to use a #foreach (and I always do).

The only object property that would require special treatment is one that is used as an href. So you can do all your filtering, mapping, etc. with #foreach loops, and keep using $object.property for output of all other properties. Then at the end create N standalone String $references to hold the URLs.

Jon_Wright
Level 3

Re: Conditional forEach statement in Velolcity Script

'Then at the end create N standalone String $references to hold the URLs.'

Slightly confused how to join this all up in my example. But will try and tackle this tomorrow, so 'basically' I need to:

Filter the list i.e.

#set( $domainProjects = [] )

#foreach( $project in $allProjects )

#if( $project.domain.equalsIgnoreCase($lead.Email_Domain__c) )

#set( $void = $domainProjects.add($project) )

#end

#end

Then create the url reference from the filtered list (with something similar to your code):

#foreach( $key in $lead.downloadcenter.split(";") )

#set( $downloadLink = $allDownloadLinks[$key] )

#evaluate( "${esc.h}set( ${esc.d}downloadLink_${foreach.count} = ${esc.d}downloadLink )" )

#end

Then have an if statement for N url placeholders, where if the link exists in the filtered list it displays, otherwise it doesn't.

(And I thought tomorrow would just be a day of colouring in this email!)##

SanfordWhiteman
Level 10 - Community Moderator

Re: Conditional forEach statement in Velolcity Script

You can do it without the #evaluate, but it still involves repetition where we'd prefer DRY.

Here's an example, also including capping the number of URLs at 8 to make sure there's no unexpected error.

#foreach( $idx in [0..$math.min(7,$math.sub($domainProjects.size(),1))] )

#set( $project = $domainProjects[$foreach.index] )

You're a ${project.category.participantType} for Project ${foreach.count}:

#if( $foreach.index.equals(0) )

#set( $project_category_URL_0 = $domainProjects[0].category.URL.substring(8) )<a href="https://${project_category_URL_0}">##

#elseif( $foreach.index.equals(1) )

#set( $project_category_URL_1 = $domainProjects[1].category.URL.substring(8) )<a href="https://${project_category_URL_1}">##

#elseif( $foreach.index.equals(2) )

#set( $project_category_URL_2 = $domainProjects[2].category.URL.substring(8) )<a href="https://${project_category_URL_2}">##

#elseif( $foreach.index.equals(3) )

#set( $project_category_URL_3 = $domainProjects[3].category.URL.substring(8) )<a href="https://${project_category_URL_3}">##

#elseif( $foreach.index.equals(4) )

#set( $project_category_URL_4 = $domainProjects[4].category.URL.substring(8) )<a href="https://${project_category_URL_4}">##

#elseif( $foreach.index.equals(5) )

#set( $project_category_URL_5 = $domainProjects[5].category.URL.substring(8) )<a href="https://${project_category_URL_5}">##

#elseif( $foreach.index.equals(6) )

#set( $project_category_URL_6 = $domainProjects[6].category.URL.substring(8) )<a href="https://${project_category_URL_6}">##

#elseif( $foreach.index.equals(7) )

#set( $project_category_URL_7 = $domainProjects[7].category.URL.substring(8) )<a href="https://${project_category_URL_7}">##

#end

${project.category.name}##

</a>

<br>

<br>

#end

As you can see, you get to reuse properties like $project.category.participantType, so all is not lost, it's just about the URL values.

Believe me, I dearly wish this were not necessary, but it is because of the way the UberSpector works.