Invalid index when adding an additional value

Hello everyone,

I am building an interface dynamically for a questionnaire based on previous responses (so I can view the previous answers and then edit them if necessary when re-reviewing). The questions are stored in one database, the items being assessed in another, and the responses to my questions in a third. So, the issue is when I add a new question and answer, I receive the following error:

Interface Definition: Expression evaluation error at function a!forEach [line 140]: Error in a!forEach() expression during iteration 25: Expression evaluation error at function a!radioButtonField [line 145]: Invalid index (25) for list: valid range is 1..24

It reads as though it's able to loop through my first 24 questions and then throws up on the new entry, but I am not constraining the index to 24 values. I believe this is because it's not handling the missing values properly (previously assessed items have no response for the new question) so I manually inserted the values into a test database and received the same error. I've copied the relevant code below and I'm hoping it's something obvious that I've simply overlooked. Any help would be much appreciated!

a!localVariables(
  /*constants*/
  local!datetime:rule!Current_Date(),
  local!user:rule!Current_LoggedInUser(),
  local!nulls: index(
    rule!qryrefernceQuestionByplatformId(
      platformI_appianId: ri!platforms.appian_id
    ).data,
    wherecontains(
      "",
      touniformstring(
        index(
          rule!qryrefernceQuestionByplatformId(
            platformI_appianId: ri!platforms.appian_id
          ).data,
          "questionOrder",
          1
        )
      )
    ),
    "questionid",
    null
  ),
  local!refereceResponse: todatasubset(
    remove(
      rule!qryrefernceQuestionByplatformId(
        platformI_appianId: ri!platforms.appian_id
      ).data,
      wherecontains(
        "",
        touniformstring(
          index(
            rule!qryrefernceQuestionByplatformId(
              platformI_appianId: ri!platforms.appian_id
            ).data,
            "questionOrder",
            1
          )
        )
      )
    ),
    a!pagingInfo(1, - 1)
  ),
  local!temp,
  local!indexforQuestions: wherecontains(
    tointeger(local!nulls),
    tointeger(
      index(
        rule!QryGetQuestionsGeneral().data,
        "questionid",
        null
      )
    )
  ),
  local!questions: if(
    or(
      isnull(local!indexforQuestions),
      count(local!indexforQuestions) = 0
    ),
    rule!QryGetQuestionsGeneral(),
    todatasubset(
      remove(
        rule!QryGetQuestionsGeneral().data,
        local!indexforQuestions
      ),
      a!pagingInfo(1, - 1)
    )
  ),
  local!radioOptions: { "Yes", "No" },
  local!nQuestions: count(local!questions.data),
  /*datasubset*/
  local!currentPlatformID: if(
    rule!APN_isBlank(ri!currentPlatformID),
    ri!platforms.appian_id,
    ri!currentPlatformID
  ),
  local!newreferenceResponse: /*if(*/
  /*rule!APN_isBlank(*/
  /*local!refereceResponse.data*/
  /*),*/
  a!forEach(
    items: enumerate(local!nQuestions) + 1,
    expression: 'type!{urn:com:appian:types}QUESTION'(
      questionid: fv!index,
      platformid: ri!platforms.appian_id,
      temp: false
    )
  ),
  local!blankIntAns: if(
    rule!APN_isBlank(local!refereceResponse),
    {},
    wherecontains(
      "",
      touniformstring(
        index(local!refereceResponse.data, "answer", 1)
      )
    )
  ),
  local!initalGridData: if(
    length(local!blankIntAns) > 0,
    remove(
      local!refereceResponse.data,
      tointeger(local!blankIntAns)
    ),
    local!refereceResponse.data
  ),
  {
    a!cardLayout(
      contents: a!gridField(
        label: "",
        labelPosition: "ABOVE",
        data: /*local!refereceResponse.data,     */
        local!initalGridData,
        columns: {
          a!gridColumn(
            label: "Questions",
            value: rule!QryGetQuestionsGeneralByquestionId(fv!row.questionid).data.questiontext[1]
          ),
          a!gridColumn(label: "Response", value: fv!row.answer)
        },
        pagesize: 25,
        showWhen: and(
          local!refereceResponse.totalCount <> 0,
          not(ri!platformEdit = true)
        ),
        validations: {}
      ),
      height: "TALL",
      showWhen: and(
        local!refereceResponse.totalCount <> 0,
        not(ri!platformEdit = true)
      ),
      style: "STANDARD"
    ),
    a!cardLayout(
      contents: a!columnsLayout(
        columns: {
          a!columnLayout(
            contents: {
              a!forEach(
                items: local!questions.data,
                expression: {
                  if(
                    fv!item.format = "Y/N",
                    a!radioButtonField(
                      label: fv!item.questiontext,
                      labelPosition: "ABOVE",
                      choiceLabels: local!radioOptions,
                      choiceValues: local!radioOptions,
                      value: ri!newreferenceResponse[fv!index].answer,
                      saveInto: {
                        ri!newreferenceResponse[fv!index].answer,
                        if(
                          ri!newreferenceResponse[fv!index].answer = "No",
                          {
                            a!save(local!temp, fv!index),
                            a!forEach(
                              a!forEach(
                                local!questions.data,
                                if(
                                  fv!item.refToParent = tostring(local!temp),
                                  fv!item.questionOrder,
                                  {}
                                )
                              ),
                              a!save(
                                ri!newreferenceResponse[fv!item].answer,
                                Null
                              )
                            )
                          },
                          {}
                        ),
                        /*a!save(local!temp,a!forEach(local!questions.data,if(fv!item.refToParent="2",fv!item.id,{}))),*/
                        a!save(
                          ri!newreferenceResponse[fv!index].questionid,
                          fv!item.questionid
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].temp,
                          ""
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].isActive,
                          fv!item.isActive
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].questionOrder,
                          fv!item.questionOrder
                        ),
                        a!save(
                          ri!newreferenceResponse.user,
                          local!user
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].datetime,
                          local!datetime
                        ),
                        a!save(
                          ri!newreferenceResponse,
                          ri!newreferenceResponse
                        )
                      },
                      showWhen: if(
                        fv!item.levelIndicator = "2",
                        if(
                          ri!newreferenceResponse[tointeger(fv!item.refToParent)].answer = "Yes",
                          true,
                          false
                        ),
                        true()
                      ),
                      required: if(
                        fv!item.levelIndicator = "2",
                        if(
                          ri!newreferenceResponse[tointeger(fv!item.refToParent)].answer = "Yes",
                          true,
                          false
                        ),
                        true()
                      ),
                      choiceLayout: "STACKED",
                      validations: {}
                    ),
                    a!textField(
                      label: fv!item.questiontext,
                      labelPosition: "ABOVE",
                      value: ri!newreferenceResponse[fv!item.questionOrder].answer,
                      saveInto: {
                        ri!newreferenceResponse[fv!item.questionOrder].answer,
                        a!save(
                          ri!newreferenceResponse[fv!index].questionid,
                          fv!item.questionid
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].temp,
                          ""
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].isActive,
                          fv!item.isActive
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].questionOrder,
                          fv!item.questionOrder
                        ),
                        a!save(
                          ri!newreferenceResponse.user,
                          local!user
                        ),
                        a!save(
                          ri!newreferenceResponse[fv!index].datetime,
                          local!datetime
                        ),
                        a!save(
                          ri!newreferenceResponse,
                          ri!newreferenceResponse
                        )
                      },
                      showWhen: if(
                        fv!item.levelIndicator = "2",
                        if(
                          ri!newreferenceResponse[tointeger(fv!item.refToParent)].answer = "Yes",
                          true,
                          false
                        ),
                        true()
                      ),
                      required: if(
                        fv!item.levelIndicator = "2",
                        if(
                          ri!newreferenceResponse[tointeger(fv!item.refToParent)].answer = "Yes",
                          true,
                          false
                        ),
                        true()
                      )
                    )
                  )
                }
              )
            }
          )
        },
        showWhen: or(
          local!refereceResponse.totalCount = 0,
          ri!platformEdit = true
        )
      ),
      height: "TALL",
      showWhen: or(
        local!refereceResponse.totalCount = 0,
        ri!platformEdit = true
      ),
      style: "STANDARD"
    )
  }
)

  Discussion posts and replies are publicly visible

Parents
  • 0
    Certified Lead Developer

    You mentioned this error happens when you add a new question/answer, but I don't see any place in your code where such addition is handled.  When doing this addition, are you also adding a corresponding item to the end of the `ri!newreferenceResponse` array?  The error indicates that its length is not matching the length of local!questions.

    I notice that local!newreferenceResponse *does* appear to be configured to follow the length of local!questions by local variable "update chaining" as i call it, but of course this is completely separate from ri!newreferenceResponse.

  • Thanks, Mike.

    I suppose that information would have been helpful. The questions aren't added in Appian (I don't have an interface built for it yet) so they are added manually to the database. Hopefully I can explain this a bit better, but the way the process works is:

    1. An asset is entered and the questions answered as applicable to that asset (static questions, mostly Yes/No)

    2. Responses are recorded in another table

    3. When the asset is re-assessed, Appian first puts together the list of questions and responses as they were entered the last time

    4. If nothing has changed I can simply submit the page and keep moving through the workflow. If it has changed, I change the questionnaire to provide the edited information, then submit

    I suspect the issue is between steps 3-4 where the question I added was not included in the previous assessment, but when I inserted responses in the DB it still errored with the same index issue.

    Does that help?

Reply
  • Thanks, Mike.

    I suppose that information would have been helpful. The questions aren't added in Appian (I don't have an interface built for it yet) so they are added manually to the database. Hopefully I can explain this a bit better, but the way the process works is:

    1. An asset is entered and the questions answered as applicable to that asset (static questions, mostly Yes/No)

    2. Responses are recorded in another table

    3. When the asset is re-assessed, Appian first puts together the list of questions and responses as they were entered the last time

    4. If nothing has changed I can simply submit the page and keep moving through the workflow. If it has changed, I change the questionnaire to provide the edited information, then submit

    I suspect the issue is between steps 3-4 where the question I added was not included in the previous assessment, but when I inserted responses in the DB it still errored with the same index issue.

    Does that help?

Children
  • 0
    Certified Lead Developer
    in reply to robinh0003

    I believe the solution requires that you do some initial formatting on the "answers" array - either prior to your user input task, or (maybe preferably) in your local variable definitions.  In the latter case, you'd want to save directly into the value of your local variable response array, rather than saving directly into the rule input as you're currently attempting; then upon click of the "submit" button, you'd push the value of the local variable back into the rule input.

    In either case the logic would be simply something like this: loop through the list of questions; look in the existing responses array for the current index.  If the index exists, keep the response at that index.  Otherwise if the index doesn't exist (i.e. you have 15 questions but only 14 extant responses, and we're now at index 15 of the questions array), declare a blank / default array entry of the "response" type.  Then when you try direct reference to a "newReferenceResponse" entry (i.e. "ri!newReferenceResponse[fv!index]"), it should no longer break.

  • I think the suggestion to "declare a blank / default array entry of the "response" type" is the root of my issue, but I'm not sure how to accomplish this. Do you know of any documentation or sample code you could point me to that I could try adapting to our environment?

    *Edit: Specifically, I'm looking at line 119 above as the possible culprit. Would you agree?

  • 0
    Certified Lead Developer
    in reply to robinh0003

    I'm not sure I see anything at line 119 in your original code box that seems problematic to me.  I was thinking more about your local variable definition at line 77 (which i notice you don't end up using anywhere subsequently in the form).  Basically your a!forEach() statement should be constructing an array of answers that correspond to your existing questions (the forEach statement already loops over the questions in about the same way that it probably ends up needing).