Simple Way to Deal with Loop Lengths

Looping through a vector and doing some task is really useful. However, it can be tricky to avoid getting an error on the last step. Rather than using if...else in a for loop, the while loop works well for this scenario.

how-to
Author

Nathan Craig

Published

March 18, 2024

Modified

March 21, 2024

Problem

Quite often we want to loop through a vector, list, or data frame of objects and do something. Often times we want to do things based on a position or, in this case, a relationship between positions in an index. I recently ran into a situation where I was getting errors while trying to loop through an index and get the values between the two lookups. While all of the individual parts of the expression worked, they returned an error when run through a for loop. I couldn’t figure out why so I started making a reproducible example to ask a question on Stack Overflow. Unsurprisingly I answered my own question in the process. Here’s what happened.

Let’s assume a lookup vector. We’re actually interested in the numbers between the lookup values (i.e. 1-5, 5-10, etc).

lookup <- c(1,5,10,14,34)
lookup
[1]  1  5 10 14 34

We can count through the number of items in the vector, all’s good.

i <- 1

lookup[1]:lookup[i+1]
[1] 1 2 3 4 5

Now let’s take those parts and run them through a loop to get the intergers between the two lookups using a loop. All of the component parts of the loop work, but the output contains an error. After some puzzling time I figured out that it actually works, but fails on the last item because there is no second placeholder when it does lookup[i+1] on the last item. The print() function was helpful in diagnosing the issue. It dawned on me that the last item fails because on that last item lookup[i+1] does not exist because it is outside the length of the list (i.e. (> length(lookup)).

In the process, I also learned that several people recommend using seq_along() rather than length(). There is also seq_len() for data frames.

i <- 1
for (i in seq_along(lookup)) {
  result <- lookup[i]:lookup[i+1]
  print(i)
  print(result)
  i = i+1
}
[1] 1
[1] 1 2 3 4 5
[1] 2
[1]  5  6  7  8  9 10
[1] 3
[1] 10 11 12 13 14
[1] 4
 [1] 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Error in lookup[i]:lookup[i + 1]: NA/NaN argument

Solution

After scratching my head and trying to place an an if...else statement, I realized that while is a much simpler solution. For the while loop, seq_along() didn’t work but length() works fine.

i <- 1
while (i < length(lookup)) {
  # if (i > length(lookup) stop("The end"))else,
  result <- lookup[i]:lookup[i+1]
  print(i)
  print(result)
  i = i+1
}
[1] 1
[1] 1 2 3 4 5
[1] 2
[1]  5  6  7  8  9 10
[1] 3
[1] 10 11 12 13 14
[1] 4
 [1] 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

Useful Resources

R Open Labs: Scripting 2 - Loops and Error Handling

Citation

BibTeX citation:
@online{craig2024,
  author = {Craig, Nathan},
  title = {Simple {Way} to {Deal} with {Loop} {Lengths}},
  date = {2024-03-18},
  url = {https://ncraig.netlify.app/posts/2024-03-18-loop-lengths/index.html},
  langid = {en}
}
For attribution, please cite this work as:
Craig, Nathan. 2024. “Simple Way to Deal with Loop Lengths.” March 18, 2024. https://ncraig.netlify.app/posts/2024-03-18-loop-lengths/index.html.