Why getValues and setValues — not a cell loop
getValues() pulls the entire range into a single JavaScript 2D array in one API call. setValues() writes it back in one call. The alternative — reading and writing each cell inside the loop with getValue() / setValue() — sends one HTTP round-trip per cell to Google's backend, which means a 200-row column triggers 400 calls instead of 2. Apps Script's execution quota is 6 minutes per run; the batched version of this function finishes a 1,000-row column in under a second. The cell-by-cell version will hit quota on anything larger than a few hundred rows.
The 2D array structure mirrors the range: values[0] is the first row, values[0][0] is the top-left cell. You mutate the array in place, then hand the whole thing back to setValues(). The range object never needs to be touched again after that first getValues() call.