Iteration inside fluidRow() with for and if statement in R

317 Views Asked by At

I am trying to use fluidRow inside a bs4TabItem that will have a maximum of 3 bs4UserCard items. The fluidRows should be built dynamically inside a for loop and the max. 3 bs4UserCard should also be built dynamically inside a for loop. Below is a snippet of the code. I have tried the code both in the UI and the Server section base on other recommendations but it still doesn't work.

# Get number of rows from dataset
records = nrow(candidatesDF)

# Each row will have max. 3 items
numRows = ceiling(nrow(candidatesDF) / 3)
numRows = c(1:numRows)

count = 0
offset = 3

candidates =  bs4TabItem(tabName = "candidates",
  for (row in numRows) {
    fluidRow(
      if (records < offset) {
        offset = records
      }
      for (column in 1:offset) {
        count = count + 1

        # Convert the names to a more legible format
        name = explode(candidatesDF[count, "Candidate"], sep = ", ")
        name = paste0(name[2], ' ', name[1])
        name = capitalizeStrings(name, all.words = TRUE, lower.back = TRUE)

        # Convert the names to the img name format
        imgName = explode(name, sep = " ")
        imgName = tolower(implode(imgName, "_"))
        imgUrl = paste0("img/", imgName, ".png")

        # Create a user card on each iteration.
        bs4UserCard(
          title = name,
          subtitle = candidatesDF[count, "Party"],
          type = NULL,
          width = 4,
          src = imgUrl,
          status = "danger",
          closable = TRUE,
          elevation = 4,
          imageElevation = 4,
          fluidRow(
            column(
              width = 4,
              descriptionBlock(header = "District",
               text = capitalizeStrings(candidatesDF[count, "District"],
                      all.words = TRUE, lower.back = TRUE ))
            ),
            column(
              width = 4,
              descriptionBlock(header = "Votes",
               text = candidatesDF[count, "Votes"])
            ),
            column(
              width = 4,
              descriptionBlock(header = "Result",
               text = candidatesDF[count, "Result"], right_border = FALSE)
            )
          )
        )

        records = records - 1
      }
    ) 
  } 
)

With the if statement I get this error

Possible missing comma at:
87:  for (row in fluidRows) {
              ^

If I remove the if statement just for testing purposes I get this error

Warning: Error in explode: Assertion on 'x' failed: May not be NA.

I'm not sure how x in explode is NA because I don't have any NA values in the dataset. When I run the code by line to test the explode function, the expected result is returned, so I don't understand why the NA.

Nonetheless my goal is to create x amount of fluidRows with a max of 3 items in each row, with the info for the items dynamically generated from the dataset. ######################################################################## I have updated the code to reflect suggestion to use lapply().

# Get number of rows from dataset
records = nrow(candidatesDF)

# Each row will have max. 3 items
numRows = ceiling(nrow(candidatesDF) / 3)
numRows = c(1:numRows)

count = 0
offset = 3

checkOffset = function(records, offset) {
  if (records < offset) {
    offset = records
  }
  
  return(offset) 
}


candidates =  bs4TabItem(tabName = "candidates",
 lapply(numRows, function(r) {
   fluidRow(
     lapply(1:checkOffset(records, offset), function(c) {
       count = count + 1
       print(count)
       # Convert the names to a more legible format
       name = explode(candidatesDF[count, "Candidate"], sep = ", ")
       name = paste0(name[2], ' ', name[1])
       name = capitalizeStrings(name, all.words = TRUE, lower.back = TRUE)

       # Convert the names to the img name format
       imgName = explode(name, sep = " ")
       imgName = tolower(implode(imgName, "_"))
       imgUrl = paste0("img/", imgName, ".png")
       
       records = records - 1
       
       # Create a user card on each iteration.
       bs4UserCard(
         title = name,
         subtitle = candidatesDF[count, "Party"],
         type = NULL,
         width = 4,
         src = imgUrl,
         status = "primary",
         closable = TRUE,
         elevation = 4,
         imageElevation = 4,
         fluidRow(
           column(
             width = 4,
             descriptionBlock(header = "District",
              text = capitalizeStrings(candidatesDF[count, "District"],
                     all.words = TRUE, lower.back = TRUE ))
           ),
           column(
             width = 4,
             descriptionBlock(header = "Votes",
              text = candidatesDF[count, "Votes"])
           ),
           column(
             width = 4,
             descriptionBlock(header = "Result",
              text = candidatesDF[count, "Result"], right_border = FALSE)
           )
         )
       )
     })
   )
 })

While this helped a great deal in getting close to the desired result, every card in each grid is the same. That is because the count variable does not increment, nor does the record variable.

Thanks in advance.

1

There are 1 best solutions below

0
On

After switching the for loop to lapply base on the recommendation by @YBS, my main issue became figuring out why the count and record variable would not keep their values after the first lapply iterates.

The answer is to use <<- for the assignment instead of =. An explanation can be found at can lapply not modify variables in a higher scope.