SOLVED

A basic foreach issue

Go to solution
Jo_Pitts1
Level 10 - Community Advisor

Basic as in simple.. not as in BASIC (sorry - old person language joke)

I have this simple piece of code

 

#set ($seminarList = "A;1|B;5|C;3")
#foreach ($myitem in $seminarList.split("[|]"))
  ${myitem}
  #end

 

And it is spitting the dummy (completely refusing to do anything useful).

I was expecting it to spit out A;1 B;5 C;3 (obviously, no styling on this yet, but that's not the current issue)

What am I managing to miss here? 

1 ACCEPTED SOLUTION
SanfordWhiteman
Level 10 - Community Moderator
#set( $allSeminarDetails = [ 
  {"CODE":"GNG","BLURB":"Come to GNG and learn about Poodles"},
  {"CODE":"POY","BLURB":"It's POYsonal Now"},
  {"CODE":"GUL","BLURB":"GULivers travels have nothing on life here"},
  {"CODE":"WTG","BLURB":"Waiting Waiting Waiting"}
] )  
#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )
#set( $currentSeminarCodes = $currentSeminarList.split("[|]") )
## this list will hold matched seminars
#set( $currentSeminarDetails = [] )
## find matching seminar in master list
#foreach( $code in $currentSeminarCodes )
#foreach( $detail in $allSeminarDetails )
#if( $detail.CODE.equals($code) )
#set( $void = $currentSeminarDetails.add($detail) )
#break
#end
#end
#end
## iterate over matches only
#foreach( $seminar in $currentSeminarDetails )
${seminar}
#end

View solution in original post

16 REPLIES 16
SanfordWhiteman
Level 10 - Community Moderator

That's the correct syntax to split on the pipe character (which is escaped by being in the character class []).

 

I assume you have this in a Velocity {{my.token}} in an email, yes?

Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman yes - this is in a token.  

I've tweaked things and made it behave, so now my next challenge.

How to take my split string, and find each matching value in an array.

I've got to this point:

 

#set( $seminarDetails = [ 
  {"CODE":"GNG","BLURB":"Come to GNG and learn about Poodles"},
  {"CODE":"POY","BLURB":"It's POYsonal Now"},
  {"CODE":"GUL","BLURB":"GULivers travels have nothing on life here"},
  {"CODE":"WTG","BLURB":"Waiting Waiting Waiting"}
  ])
  
#set ($seminarList = ${lead.metlifecompetitiondatas2})
#set ($seminarArray = $seminarList.split("[|]"))
#foreach ($seminarCode in $seminarArray)
  ## how to find the SeminarCode in the SeminarDetails array.
  #end

 

 

Let's assume the passed in field {lead.metlifecompetitiondatas2} contains POY|GUL.

My foreach loop will iterate twice (happy day).  What I want to do is get access to the appropriate blurb in the array at the top for output in the email (without doing some silly nested foreach loop on the seminarDetails array).

And I am sure I could condense some of this (i.e. splitting the marketo field straight into an array), but I like to keep it nice and stepwise so I can understand each bit before condensing once I have a handle on what is going on.

SanfordWhiteman
Level 10 - Community Moderator
#set( $allSeminarDetails = [ 
  {"CODE":"GNG","BLURB":"Come to GNG and learn about Poodles"},
  {"CODE":"POY","BLURB":"It's POYsonal Now"},
  {"CODE":"GUL","BLURB":"GULivers travels have nothing on life here"},
  {"CODE":"WTG","BLURB":"Waiting Waiting Waiting"}
] )  
#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )
#set( $currentSeminarCodes = $currentSeminarList.split("[|]") )
## this list will hold matched seminars
#set( $currentSeminarDetails = [] )
## find matching seminar in master list
#foreach( $code in $currentSeminarCodes )
#foreach( $detail in $allSeminarDetails )
#if( $detail.CODE.equals($code) )
#set( $void = $currentSeminarDetails.add($detail) )
#break
#end
#end
#end
## iterate over matches only
#foreach( $seminar in $currentSeminarDetails )
${seminar}
#end
SanfordWhiteman
Level 10 - Community Moderator

If you used String keys in the Map it would be much shorter, fwiw:

#set( $allSeminarDetails = {
  "GNG" : {
    "BLURB":"Come to GNG and learn about Poodles"
  },
  "POY" : {
    "BLURB":"It's POYsonal Now"
  },
  "GUL" : {
    "BLURB" : "GULivers travels have nothing on life here"
  },
  "WTG" : {
    "BLURB" : "Waiting Waiting Waiting"
  }
} )  
#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )
#set( $currentSeminarCodes = $currentSeminarList.split("[|]") )
## this list will hold matched seminars
#set( $currentSeminarDetails = [] )
## find matching seminar in master list
#foreach( $code in $currentSeminarCodes )
#set( $void = $currentSeminarDetails.add($allSeminarDetails[$code]) )
#end
## iterate over matches only
#foreach( $seminar in $currentSeminarDetails )
${seminar}
#end
Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman ,

I can switch to string keys quite happily and easily 🙂

I'm always happy to find nicer/better ways of doing things especially as it avoids the ghastly nested loop.

How far off the mark am I in my desire to add more than just the details from allSeminarDetails (i.e. + the number of attendees) in my previous message.

SanfordWhiteman
Level 10 - Community Moderator

I'm always happy to find nicer/better ways of doing things especially as it avoids the ghastly nested loop.

It's not always better, as there may be reasons where having a string key isn't flexible enough. But for this case it'd be better.

 

What do you mean by "and number of attendees"?

Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman  (and for the general amusement and hopefully edification of others),

I've made solid progress.  My code is working as expected, and I'm getting to grips with the string maps stuff quite nicely.

The output section at the end is almost 'debug' at the moment, but will go into a nice table shortly.

 

#set( $allSeminarDetails = {
  "GNG" : {
    "blurb":"Come to GNG and learn about Poodles",
    "date":"5 Aug 2020",
    "attendees":0
  },
  "POY" : {
    "blurb":"It's POYsonal Now",
    "date":"6 Aug 2020",
    "attendees":0
  },
  "GUL" : {
    "blurb" : "GULivers travels have nothing on life here",
    "date":"7 Aug 2020",
    "attendees":0
  },
  "WTG" : {
    "blurb" : "Waiting Waiting Waiting",
    "date":"8 Aug 2020",
    "attendees":0
  },
  "POH" : {
    "blurb" : "POH POH POH your boat",
    "date":"9 Aug 2020",
    "attendees":0
  }
} )  
#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )
#set( $currentSeminarCodes = $currentSeminarList.split("[|]") )
## this list will hold matched seminars
#set( $currentSeminarDetails = [] )
## find matching seminar in master list
#foreach( $code in $currentSeminarCodes )
  #set( $currentRegistration = $code.split("[;]") )
  #set( $villageCode = $currentRegistration[0] )
  #set( $attendees = $currentRegistration[1] )
  #set( $aSeminar = $allSeminarDetails[$villageCode]) 
  #set( $aSeminar.attendees = $currentRegistration[1] )
  #set( $void = $currentSeminarDetails.add( $aSeminar ) )
  #end
## iterate over matches only
#foreach( $seminar in $currentSeminarDetails )
  ${seminar.blurb}  
  ${seminar.date}  
  ${seminar.attendees}<BR><BR>
  #end

 

What I'm most curious about is where can I improve what I'm doing.  Are their areas of inelegance or inefficiency in here that I could be doing better?

 

Cheers

Jo

SanfordWhiteman
Level 10 - Community Moderator
  • You don't need to pre-set attendees in the details Map, though it's nicely informative if you do.
  • You aren't using this variable: 
      #set( $attendees = $currentRegistration[1] )​
  • Splitting on just ; is fine, you don't need to put it in a character class [;] as it's not reserved
Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman wrote:
  • You don't need to pre-set attendees in the details Map, though it's nicely informative if you do.
  • You aren't using this variable: 
      #set( $attendees = $currentRegistration[1] )​
  • Splitting on just ; is fine, you don't need to put it in a character class [;] as it's not reserved

  • I like being informative, especially in a language I'm not super familiar with.  It helps me understand my work later.  That being said, I take your point about it not being needed.
  • True enough.  That line can go
  • Gotcha.  Makes perfect sense.

Now time to do a gloriously formatted table for output, and I'm most of the way there! 🙂

 

Thanks so much for all your help @SanfordWhiteman .

 

Cheers

Jo

Jo_Pitts1
Level 10 - Community Advisor

Hmm... so my code now looks like this (and seems to be working)

#set( $allSeminarDetails = {
  "ABC" : {
    "blurb":"Postman Pat",
    "date":"24 Aug",
    "time":"10:30",
    "attendees":0,
    "village":"somewhere"
  },
  "CBA" : {
    "blurb":"Bob the builder",
    "date":"14 Aug",
    "time":"10:30",
    "attendees":0,
    "village":"somewhere else"
  }
  
} )  
#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )
#set( $currentSeminarCodes = $currentSeminarList.split("[|]") )
## this list will hold matched seminars
#set( $currentSeminarDetails = [] )
## find matching seminar in master list
#foreach( $code in $currentSeminarCodes )
  #set( $currentRegistration = $code.split(";") )
  #set( $villageCode = $currentRegistration[0] )
  #set( $aSeminar = $allSeminarDetails[$villageCode]) 
  #set( $aSeminar.attendees = $currentRegistration[1] )
  #set( $void = $currentSeminarDetails.add( $aSeminar ) )
  #end
## iterate over matches only
#foreach( $seminar in $currentSeminarDetails )
  <table style="table-layout:fixed; width:100%; background:#eeeeee">
  <tr>
  <td style="width:30%;">Date</td><td style="">${seminar.date}</td>
  </tr>
  <tr>
  <td>Time</td><td style="">${seminar.time}</td>
  </tr>
  <tr>
  <td>Village</td><td style="">${seminar.village}</td>
  </tr>
  <tr>
  <td>Details</td><td style="">${seminar.blurb}</td>
  </tr>
  <tr>
  <td>Attendees</td><td style="">${seminar.attendees}</td>
  </tr>
  </table>  
  <br>
  #end

But I get the following error when I try to either approve the email or send a sample.

Validation Error approving LG - 202007 - RRS - CORE.01 - Registration Acknowledgement —  <div>An error occurred when procesing the email Rendered_Email_Velocity_Error_Area_?! </div> <p>Error invoking method 'get(java.lang.Integer)' in [Ljava.lang.String; near</p> <div><pre >?</pre></div>

 

I have made sure I don't have the velocity in my text version of the email.  What should I look for next?

Cheers

Jo

SanfordWhiteman
Level 10 - Community Moderator

You haven't accounted for the nulls that happen when the lead field is empty (which is the case when you approve).

Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman ,

gotcha.  I presume the easiest way to do this is to wrap all the code after this

#set( $currentSeminarList = $lead.metlifecompetitiondatas2 )

with this

#if( $currentSeminarList != "" )
  All the doing code 
  #end

It seems to behave, but is that the best practice approach?

 

SanfordWhiteman
Level 10 - Community Moderator

To be most proper:

 

#if( $display.alt($currentSeminarList,"").isEmpty() )

 

While in practice a lead field can only be empty, not null, this isn't the case for CO fields and you should be ready for both cases. 

Jo_Pitts1
Level 10 - Community Advisor

Then most proper we shall be.

Jo_Pitts1
Level 10 - Community Advisor

@SanfordWhiteman 

So, in my string, I actually get something like:

GNG;4|POY;3

 

The code gives me the seminar, and the number is the attendees.

Ultimately, my allSeminarDetails map will have blurb, date, time, and a URL.

My currentSeminarDetails will end up with the right rows from allSeminarDetails PLUS the attendees for that seminar.

That way I can emit it all in the HTML of the email.

Sorry for not making this all clear at the beginning - I was trying to give a simplified example and build it up from there.

Jo_Pitts1
Level 10 - Community Advisor

So a nested loop in other words :).... Darn it I was hoping for something more elegant!  Thanks for the help @SanfordWhiteman 

Next silly question.  in this line here:

#set( $void = $currentSeminarDetails.add($detail) )

How can I add one extra element that is NOT in the $allSeminarDetails array.  In this instance, I also know the number of attendees (which can vary by seminar).

 

So in essence what I want (and will experiment with shortly) is something like

#set( $void = $currentSeminarDetails.add($detail,$attendees) )

but I feel like that won't work