Skip to main content
beenav4597
November 18, 2022
Solved

Can we exit from a!forEach() loop?

  • November 18, 2022
  • 17 replies
  • 0 views

Hi Everyone,

Can we exit from a!forEach() loop? 

I have a use case , where I need to iterate through the list of users and once the condition met ( checking for the user's organization and weight category), I have to stop iterating through the rest of the items. I need order to achieve this, I have to exit from the loop. 

How can I achieve this ?

Apologies for not clarifying the use case earlier.

Use case is given below.

Use Case: There is a list of Boxers. Each boxers belongs to particular organization and weight category. I want to select 2 boxers for the match randomly. 2 boxers must belong to same weight category but different organization.

To achieve this I was trying to loop through list of boxers and fetch 2 boxers belonging to same weight category but different organization. Once I get 2 different boxers, then I wanted to exit the loop, because I got 2 boxers for the match and don't want to loop further. Note: Weight category and organization are not known initially. That also needs to be picked randomly.

Please do let me know if this is still not clear.

Best answer by eunnelarciee103779

Yes, I made it like this

a!localVariables(
  local!boxer: a!queryRecordType(
    recordType: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers',
    fields: a!aggregationFields(
      groupings: {
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{f4e1f3f8-bc8c-4432-a327-8add89ee8307}categoryId',
          alias: "categoryId"
        ),
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{abb9bb86-46e6-4e5f-b949-6aec81eaa7b6}organization',
          alias: "organization"
        ),
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{0cf8dde6-3347-44c2-b9a6-777ac23cd35e}name',
          alias: "boxerName"
        )
      }
    ),
    pagingInfo: a!pagingInfo(
      startIndex: 1,
      batchSize: 50
    ),
    fetchTotalCount: true
  ),
  
  rule!MFA_BoxerIterations(boxer: local!boxer.data, totalCount: local!boxer.totalCount)
)

MFA_BoxerIterations

a!localVariables(
  local!count: ceiling(rand() * (ri!totalCount-1)),
  local!index: if(
    local!count >= ri!totalCount,
    ri!totalCount - 1,
    local!count
  ),
  local!match: ri!boxer[tointeger({ local!index, local!index + 1 })],
  /*local!match: index(ri!boxer, tointeger({ local!index, local!index + 1 }), null),*/
  
  if(
    local!match.categoryId[1] <> local!match.categoryId[2],
    rule!MFA_BoxerIterations(boxer: ri!boxer, totalCount: ri!totalCount),
    local!match
  )
)


Result:

Hope this helps.

17 replies

harshj0001
November 18, 2022

Hi beenav4597,

I don't think that you can exit the for loop on the specific condition.

What can you do that save the null value when the condition is false and save the value on the true condition and outside the loop you can use the reject function for getting the desire output.

a!localVariables(
  local!test : {
    1,2,3,4
  },
  reject(
    isnull(_),
    a!forEach(
      items: local!test,
      expression: if(
        fv!item = 2,
        fv!item,
        null()
      )
    )
  )
)


You can use wherecontain() function if it fulfills the condition.

https://docs.appian.com/suite/help/22.3/fnc_array_wherecontains.html#:~:text=When%20to%20use%20wherecontains(),field%20in%20that%20CDT%20array.

a!localVariables(
  local!test : {
    1,2,3,4
  },
  local!index : wherecontains(
    2,
    local!test
  ),
  if(
    a!isNullOrEmpty(
      local!index
    ),
    "",
    property(
      local!test, local!index
    )
  )
)

Hopefully it will help.

beenav4597
November 18, 2022

Thanks [mention:c09c97ff6d554cfc954848f73216d0f4:e9ed411860ed4f2ba0265705b8793d05] for the reply. Does that mean I'll have to iterate through all the items in the list even if the condition has met early?

harshj0001
November 18, 2022

Yes, if you are using for loop, then it will automatically iterate itself.
If you can elaborate your use case, then we can find an alternative for that.

stefanhelzle0001
November 18, 2022

Not. There is a reason it is called forEach. Depending in your input data there might be other options.

beenav4597
November 18, 2022

Thanks @stefan. However, just wanted to know is there any option in Appian to do this? Basically looking for while loop functionality.

mikes0011
Brainy
November 18, 2022

You can manually design a looping flow in a process model which exits as soon as a condition is met.

csteward
November 18, 2022

This can be achieved with recursion.  For an example below, run rule!chris_recur() and it will loop only until it returns a user in org "B" with weight 22 (4th user).

rule!chris_recur()

a!localVariables(
  local!users: {
    a!map(user: "Tim", org: "A", weight: 10),
    a!map(user: "Steve", org: "B", weight: 2),
    a!map(user: "Sally", org: "A", weight: 14),
    a!map(user: "Mary", org: "B", weight: 22),
    a!map(user: "George", org: "C", weight: 5),
    a!map(user: "Alex", org: "C", weight: 7)
  },
  rule!chris_recur_sub(
    users: local!users,
    index: 1
  )
)

rule!chris_recur_sub(users,index)

if(
  and(
    ri!users[ri!index].org="B",
    ri!users[ri!index].weight=22
  ),
  ri!users[ri!index], /* Exit - return user */
  if(
    ri!index=count(ri!users), /* Exit - return null/none found */
    null,
    rule!chris_recur_sub( /* Recur to next user */
      users: ri!users,
      index: ri!index+1
    )
  )
)

Always remember to save your recursive functions before testing them!

beenav4597
November 20, 2022

Hi [mention:c09c97ff6d554cfc954848f73216d0f4:e9ed411860ed4f2ba0265705b8793d05],[mention:126a676c85024855948336691b0a7b92:e9ed411860ed4f2ba0265705b8793d05],[mention:b45a4f6a20b14a008e4e0ec732a8bf98:e9ed411860ed4f2ba0265705b8793d05],[mention:23a2ab7d11d24e69bfaa8e50e14d1914:e9ed411860ed4f2ba0265705b8793d05]

I have updated the use case now.

November 21, 2022

I guess you can achieve it by using queryAggregation or GROUP BY if done in SQL script, and add randomizer for index.

Something similar to this:

a!localVariables(
  local!boxers: a!queryEntity(
    entity: cons!MFA_BOXER_LIST_DSE,
    query: a!query(
      aggregation: a!queryAggregation(
        aggregationColumns: {
          a!queryAggregationColumn(
            field: "organization",
            isGrouping: true
          ),
          a!queryAggregationColumn(
            field: "categoryId",
            isGrouping: true
          ),
          a!queryAggregationColumn(
            field: "id",
            aggregationFunction: "MAX"
          )
        }
      ),
      pagingInfo: a!pagingInfo(
        startIndex: 1,
        batchSize: 50
      )
    ),
    fetchTotalCount: true
  ),
  
  local!count: local!boxers.totalCount - 1,
  local!index: odd(rand() * local!count),
  
  local!boxers.data[{local!index, local!index + 1}]
)

beenav4597
November 21, 2022

Thanks for the response. This would return boxer under groups(Organization & Weight Category) . However, I still need to go through the list of boxers to get a match isn't it?

November 22, 2022

Yes, I made it like this

a!localVariables(
  local!boxer: a!queryRecordType(
    recordType: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers',
    fields: a!aggregationFields(
      groupings: {
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{f4e1f3f8-bc8c-4432-a327-8add89ee8307}categoryId',
          alias: "categoryId"
        ),
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{abb9bb86-46e6-4e5f-b949-6aec81eaa7b6}organization',
          alias: "organization"
        ),
        a!grouping(
          field: 'recordType!{74852bd6-d06a-4435-b608-70b4779e96eb}MFA Boxers.fields.{0cf8dde6-3347-44c2-b9a6-777ac23cd35e}name',
          alias: "boxerName"
        )
      }
    ),
    pagingInfo: a!pagingInfo(
      startIndex: 1,
      batchSize: 50
    ),
    fetchTotalCount: true
  ),
  
  rule!MFA_BoxerIterations(boxer: local!boxer.data, totalCount: local!boxer.totalCount)
)

MFA_BoxerIterations

a!localVariables(
  local!count: ceiling(rand() * (ri!totalCount-1)),
  local!index: if(
    local!count >= ri!totalCount,
    ri!totalCount - 1,
    local!count
  ),
  local!match: ri!boxer[tointeger({ local!index, local!index + 1 })],
  /*local!match: index(ri!boxer, tointeger({ local!index, local!index + 1 }), null),*/
  
  if(
    local!match.categoryId[1] <> local!match.categoryId[2],
    rule!MFA_BoxerIterations(boxer: ri!boxer, totalCount: ri!totalCount),
    local!match
  )
)


Result:

Hope this helps.

andresalbertoy0001
April 15, 2025

Hi everyone, 

this post doesn't provide any resolution to this topic, you can skip it if you want, here is just a bit of my bitter frustration :D 


Every now and then I find myself returning to this thread.

Hopefully, one day the miracle will happen, and Appian will include a break or continue in the foreach cycle. Or why not, do...while.

I can say that we could avoid a lot of unnecessary iteration if we had this, or for someone average like me, to be able to stop the looping simply, without resorting to recursion, sinister strategies or endless trial and error to limit the looping when you need it.... simply and easily (please notice the remarks about SIMPLICITY).

And I know that some people think “No. There is a reason it is called forEach”, but in other languages, someone (probably smarter/experienced than the average common citizen like me) did not agree and has thought of including the break for the foreach...just saying. 

Yes, yes, yes, I know, SAIL is declarative and there will be a million reasons to justify why we don't have a simple break in SAIL, but the result is that we don't have it.... and we'll have to deal with it.

Maybe something Appian could help with is shaping a recipe to deal with this challenge (whisper: hey, if there is any already, post it in this thread and mitigate my ignorance)

I'll be back here again in a couple of weeks....no doubt.

stefanhelzle0001
April 15, 2025

Hm ... what are you missing? I mean, you say that you are missing other looping functions, but what is it that you cannot achieve?