R Programming Quiz: Naming And Formatting
10 questions · exam conditions
0:00
Naming And FormattingQuestion 1 of 10

A function receives equal-length vectors containing payment amounts and payment statuses:

f <- function(x, y) { z <- x[y == "paid"]; sum(z, na.rm = TRUE) }

The team uses snake_case and prefers names that describe the business meaning of a value.

Which refactoring best improves naming and formatting while preserving the function's behavior?

total_paid <- function(amounts, statuses) { paid_amounts <- amounts[statuses == "paid"]; sum(paid_amounts, na.rm = TRUE) }
total_paid <- function(amounts, statuses) { paid_statuses <- statuses[amounts == "paid"]; sum(paid_statuses, na.rm = TRUE) }
total_paid <- function(amounts, statuses) { unpaid_amounts <- amounts[statuses != "paid"]; sum(unpaid_amounts, na.rm = TRUE) }
total_paid <- function(amounts, statuses) { paid_amounts <- amounts[statuses == "paid"]; sum(paid_amounts) }
← Back to quizzes

R Programming Quiz

R Programming Quiz: Naming And Formatting

Practice Naming And Formatting in R Programming with focused quiz questions that help you check what you know, review explanations, and build confidence with test-style prompts.

What this quiz covers

This quiz focuses on Naming And Formatting, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.

How to use this quiz

Try each quiz question before looking at the correct answer. Use the explanations to review missed ideas, then come back to similar questions until the pattern feels familiar.

All questions

Question 1

A function receives equal-length vectors containing payment amounts and payment statuses:

f <- function(x, y) { z <- x[y == "paid"]; sum(z, na.rm = TRUE) }

The team uses snake_case and prefers names that describe the business meaning of a value.

Which refactoring best improves naming and formatting while preserving the function's behavior?

  1. total_paid <- function(amounts, statuses) { paid_amounts <- amounts[statuses == "paid"]; sum(paid_amounts, na.rm = TRUE) } (correct answer)
  2. total_paid <- function(amounts, statuses) { paid_statuses <- statuses[amounts == "paid"]; sum(paid_statuses, na.rm = TRUE) }
  3. total_paid <- function(amounts, statuses) { unpaid_amounts <- amounts[statuses != "paid"]; sum(unpaid_amounts, na.rm = TRUE) }
  4. total_paid <- function(amounts, statuses) { paid_amounts <- amounts[statuses == "paid"]; sum(paid_amounts) }
Explanation: When refactoring code, you need to check three things simultaneously: does the logic still work, are the names meaningful in context, and does the style match team conventions? Missing any one of these disqualifies a candidate. The original function filters a vector x by checking y == "paid", then sums the result. A correct refactor must preserve exactly that logic while improving readability. Option A does this perfectly — total_paid names the function's business purpose, amounts and statuses describe what each parameter actually holds, and paid_amounts clearly names the filtered subset. The logic amounts[statuses == "paid"] mirrors the original exactly, and na.rm = TRUE is preserved. This is your correct answer. Option B has a subtle but critical bug: it indexes statuses using amounts == "paid", which reverses the roles of the two vectors. You'd be filtering the status vector using a condition applied to payment amounts — logically backwards and almost certainly wrong in practice. Option C changes the filtering condition from == "paid" to != "paid", which means it sums unpaid amounts instead of paid ones. Despite having clean naming, it produces the opposite business result. Option D looks nearly correct but silently drops na.rm = TRUE from the sum() call — if any NA values exist in the data, the function will return NA instead of a numeric total, changing the behavior in edge cases. A good study habit here: when evaluating refactors, check the logic before the names. A beautifully named function that computes the wrong thing is worse than an ugly one that works.

Question 2

A team requires collection names to be plural, one-item names to be singular, and function-valued arguments to end in _fn. The following helper selects elements satisfying a predicate:

select_items <- function(data, fun) { keep <- vapply(data, function(x) fun(x), logical(1)); data[keep] }

Which set of replacement names follows all three conventions and communicates the helper's roles most clearly?

  1. Rename data to records, fun to rule_fn, x to record, and keep to matches. (correct answer)
  2. Rename data to record, fun to rule_fn, x to records, and keep to matches.
  3. Rename data to records, fun to rule, x to record, and keep to result_fn.
  4. Rename data to records_fn, fun to rule, x to item_list, and keep to matches.
Explanation: When naming variables in R, you should evaluate each name against every convention simultaneously — plural collections, singular single-items, and _fn suffix for function arguments. A single violation disqualifies the entire set. Option A satisfies all three rules cleanly: records is plural (a collection), rule_fn carries the _fn suffix (it's a function argument), record is singular (the loop iterates one element at a time), and matches is a plain plural noun describing the logical index vector. Each name also communicates its role — you immediately understand that rule_fn is a filtering predicate and records is the input collection. This makes A the correct answer. Option B fails because data is renamed to record (singular) even though it holds the full collection, and the loop variable x becomes records (plural) even though it holds a single element at a time — exactly backwards from the singular/plural convention. Option C breaks two rules: fun is renamed to rule without the required _fn suffix (it is a function-valued argument), and keep becomes result_fn, incorrectly applying _fn to a logical vector that is not a function at all. Option D applies _fn to data by renaming it records_fn, which is wrong — data is a collection, not a function. It also renames fun to rule (missing _fn) and uses item_list for the loop variable, which is both plural and misleadingly suggests a list type. As a study strategy, treat naming conventions as a checklist: verify every variable against every rule before accepting an option, because distractors are usually designed to get two out of three rules right while hiding one violation.

Question 3

A project uses snake_case for all object names and requires repeated configuration values to have names that include their units when applicable. The current retry code is:

if (retryCount < 3L) { Sys.sleep(5); retry_request() }

Which revision follows both conventions and communicates the retry policy most clearly?

  1. MAX_RETRIES <- 3L; RETRY_DELAY_SECONDS <- 5; if (retry_count < MAX_RETRIES) { Sys.sleep(RETRY_DELAY_SECONDS); retry_request() }
  2. max_retries <- 3L; retry_delay_seconds <- 5; if (retry_count < max_retries) { Sys.sleep(retry_delay_seconds); retry_request() } (correct answer)
  3. retry_limit <- 3L; sleep_duration <- 5; if (sleep_duration < retry_limit) { Sys.sleep(sleep_duration); retry_request() }
  4. max_retries <- 3L; retry_delay_milliseconds <- 5; if (retry_count < max_retries) { Sys.sleep(retry_delay_milliseconds); retry_request() }
Explanation: When a project enforces naming conventions, every object name must satisfy all stated rules simultaneously — here, snake_case formatting and units included in names for configuration values. Keep both constraints in mind as a checklist before evaluating any option. Option B is the clean winner. It uses snake_case throughout (max_retries, retry_delay_seconds, retry_count), names the magic numbers with meaningful identifiers, includes the unit "seconds" in the delay variable (satisfying the units requirement), and the conditional logic correctly compares retry_count < max_retries — exactly the intended retry policy. Option A fails the most fundamental requirement: it uses SCREAMING_SNAKE_CASE (MAX_RETRIES, RETRY_DELAY_SECONDS). While that style is common in some languages for constants, the project explicitly requires snake_case for all object names. Violating an explicit convention disqualifies it immediately, regardless of how readable it looks. Option C has a critical logic bug that goes beyond naming: the condition reads if (sleep_duration < retry_limit), which compares a delay duration to a retry count — these measure completely different things. This would produce nonsensical behavior at runtime, and sleep_duration also omits units from the name. Option D looks close to B but introduces a subtle inaccuracy: Sys.sleep() accepts seconds, not milliseconds, so naming the variable retry_delay_milliseconds when it stores 5 (seconds) is factually misleading — the unit in the name contradicts the actual unit of the value. Study tip: On naming-convention questions, eliminate options that violate any single rule first — then check remaining options for accuracy in logic and semantics.

Question 4

A pipeline first filters raw orders to paid orders and then adds a row-level net column:

x <- subset(raw_orders, status == "paid") y <- transform(x, net = amount - fee) write.csv(y, "paid_orders.csv", row.names = FALSE)

The team wants intermediate names to describe the value at each stage rather than merely the operation performed.

Which pair of replacement names best distinguishes the two stages without implying that either stage is aggregated?

  1. Rename x to orders_with_net and y to paid_orders_before_net.
  2. Rename x to filter_result and y to transform_result.
  3. Rename x to paid_order_totals and y to net_revenue_total.
  4. Rename x to paid_orders and y to paid_orders_with_net. (correct answer)
Explanation: When naming intermediate objects in a pipeline, the goal is to describe what the data represents at that stage — not how it got there, and not what it will eventually become. Each name should be a snapshot of the data's current state. Option D achieves this perfectly. paid_orders accurately describes the filtered dataset: these are orders, and they've been filtered to only paid ones. Then paid_orders_with_net captures the next state precisely — it's still the same paid orders, but now each row includes the computed net column. Together, the names form a clear progression without implying any summarization or aggregation has occurred. Option A reverses the logic entirely, naming the filtered result orders_with_net (which doesn't yet have a net column) and the transformed result paid_orders_before_net (which already does). This would actively mislead anyone reading the code. Option B falls into the trap the question explicitly warns against: filter_result and transform_result describe the operations, not the data. They tell you what was done, not what you're holding — which is exactly the anti-pattern being avoided here. Option C introduces words like totals and total in both names, implying that aggregation (such as sum() or aggregate()) has taken place. Since neither step aggregates rows, these names misrepresent the data's structure and would cause real confusion. As a study tip, whenever you're naming intermediate pipeline objects, ask yourself: "If someone read this name without seeing the code, would they accurately picture the shape and content of this data?" If the name implies the wrong operation or structure, it's wrong.

Question 5

An analysis computes a Pearson correlation after excluding pairs with missing values:

effect <- cor(customers$age, customers$income, use = "complete.obs", method = "pearson")

The value will be passed to another analyst without surrounding documentation.

Which replacement name communicates the statistic and its data handling without making an unsupported causal claim?

  1. pearson_age_effect_on_income_complete
  2. pearson_age_income_correlation_complete (correct answer)
  3. age_causes_income_for_complete_customers
  4. customer_age_income_result_complete
Explanation: When naming a variable that will be read by others without surrounding context, you need the name to answer three questions at a glance: what statistic was computed, what variables were involved, and how missing data was handled — without implying anything beyond what the data actually supports. Option B, pearson_age_income_correlation_complete, does exactly this. "pearson" identifies the method, "age_income" names the two variables symmetrically, "correlation" states the statistic type, and "complete" signals that only complete observation pairs were used. Anyone receiving this value immediately understands what it represents. Option A, pearson_age_effect_on_income_complete, introduces the word "effect," which implies directionality or causation. Correlation is symmetric — it makes no directional claim — so "effect on" misrepresents the statistic. This is a meaningful error, not just a style issue. Option C, age_causes_income_for_complete_customers, makes an outright causal claim ("causes"), which correlation never supports. This is perhaps the most dangerous distractor because it embeds a logical fallacy directly into the variable name, which could mislead anyone reading the code later. Option D, customer_age_income_result_complete, is vague in a different way. "Result" tells you nothing about what kind of statistic was computed — it could be a mean, a regression coefficient, or anything else. Specificity is lost. A good study tip: whenever you write a variable name for a statistical result, ask yourself whether the name could be mistaken for a different statistic or a causal claim. If the answer is yes, revise it. Correlation names should be symmetric and method-labeled, never directional.

Question 6

An internal helper currently uses a negatively worded Boolean argument:

fetch_data <- function(disable_cache = FALSE) { if (!disable_cache) read_cache() else download_data() }

All call sites will be updated during the refactoring.

Which rewrite gives the Boolean a positive, meaningful name and preserves the original default behavior?

  1. fetch_data <- function(use_cache = FALSE) { if (use_cache) read_cache() else download_data() }
  2. fetch_data <- function(skip_cache = TRUE) { if (skip_cache) read_cache() else download_data() }
  3. fetch_data <- function(use_cache = TRUE) { if (use_cache) read_cache() else download_data() } (correct answer)
  4. fetch_data <- function(cache_disabled = FALSE) { if (!cache_disabled) read_cache() else download_data() }
Explanation: When refactoring Boolean arguments, you need to track two things simultaneously: the naming convention (positive vs. negative phrasing) and the default behavior (what happens when no argument is supplied). The original function defaults to disable_cache = FALSE, meaning caching is on by default — read_cache() runs unless explicitly overridden. A good rewrite should express the same intent with a positively worded name. If you rename the concept to use_cache, the default should be TRUE to preserve the original behavior (cache is used by default). The body then reads naturally: if (use_cache) read_cache() else download_data() — no double negation, clean logic. That's exactly what C does, making it the correct answer. A uses the right positive name use_cache, but sets the default to FALSE, which reverses the behavior — now caching is off by default, breaking every existing call site that relied on the original default. B uses skip_cache = TRUE, which is still a negatively oriented name (skipping implies absence of something), and the body's logic is inverted — if (skip_cache) read_cache() would read the cache when skipping it, which is nonsensical. D uses cache_disabled, which is still a negatively worded name (it just drops the "dis" prefix from "disable" but retains the negative framing), so it fails the "positive name" requirement even though the logic itself is technically correct. As a strategy, always trace the default value through the function body to confirm behavior is preserved — a name change that flips the default is a silent bug.

Question 7

A developer intended to write a function that returns the mean of values above a threshold:

filter <- function(data, mean) { mean(data[data > mean]) }

Calling the function with a numeric second argument fails because that argument masks the function named mean. The team also wants the function name to state the summary it returns.

Which refactoring both resolves the naming problem and most accurately communicates the function's result?

  1. mean_above_threshold <- function(values, threshold) { max(values[values > threshold]) }
  2. filter_above_threshold <- function(values, mean) { mean(values[values > mean]) }
  3. mean_above_threshold <- function(values, threshold) { mean(values[values > threshold]) } (correct answer)
  4. filter_above_threshold <- function(values, threshold) { base::mean(values[values > threshold]) }
Explanation: When writing R functions, two distinct problems can arise with naming: variable masking (when a parameter name shadows a built-in function) and unclear function naming (when the function's name doesn't reflect what it returns). This question tests whether you can identify and fix both simultaneously. The original code fails because the parameter mean masks R's built-in mean() function — inside the function body, mean refers to the numeric threshold argument, not the aggregation function. The fix requires renaming that parameter to something neutral like threshold. Additionally, the function name filter only describes the filtering step, not the final result, which is a mean value. Option C, mean_above_threshold <- function(values, threshold) { mean(values[values > threshold]) }, resolves both issues cleanly. The parameter threshold no longer conflicts with mean(), so the built-in function works correctly. The function name mean_above_threshold precisely communicates that the output is a mean computed over values exceeding the threshold. Option A uses the right function name and parameter but replaces mean() with max() inside the body — this computes the maximum, not the mean, directly contradicting the function's stated purpose. Option B renames the function to filter_above_threshold, which still doesn't communicate that a mean is returned, and critically keeps mean as the parameter name, so the masking bug persists. Option D fixes the masking by qualifying base::mean(), but using namespace qualification is a workaround rather than a clean solution, and filter_above_threshold still doesn't reflect the summary being computed. As a study tip: whenever you see a function parameter sharing a name with a base R function, treat it as a bug — renaming the parameter is almost always the cleaner fix over namespace qualification.

Question 8

A list named user contains several user records, each with an email field. The current loop works but uses misleading singular and plural names:

for (users in user) { send_message(users$email) }

Which rewrite most clearly distinguishes the collection from each element while preserving behavior?

  1. for (users in users) { send_message(users$email) }, after renaming the list and loop variable to users.
  2. for (user_records in user_record) { send_message(user_records$email) }, after renaming the list to user_record.
  3. for (email in user_records) { send_message(email$email) }, after renaming the list to user_records.
  4. for (user_record in user_records) { send_message(user_record$email) }, after renaming the list to user_records. (correct answer)
Explanation: When writing loops in R, clarity comes from making the relationship between a collection and its elements immediately obvious. A well-named loop follows the pattern: plural collection → singular element. This lets anyone reading your code instantly understand that each iteration pulls one item from the larger group. Option D nails this convention. After renaming the list to user_records, the loop reads for (user_record in user_records), which clearly signals "for each single record, drawn from the full collection of records." Accessing user_record$email is then perfectly natural — you're pulling the email from one record at a time. Behavior is preserved because the logic is identical to the original; only the names changed. Option A collapses both names into users, so the loop variable and the collection would be indistinguishable — R actually allows this syntactically, but it creates dangerous ambiguity and is the exact problem the question asks you to fix. Option B inverts the naming logic: the collection is named user_record (singular) and the loop variable is user_records (plural), which is backwards and implies the element is the collection. Option C uses email as the loop variable but accesses email$email — not only is the field-name redundancy confusing, but naming a whole user object email misrepresents what it actually contains, breaking semantic clarity even if the code runs. A useful study tip: whenever you write a loop over a collection, ask yourself "can a reader immediately tell which name is the bucket and which is one item?" The singular-inside-plural pattern (item in items) answers that question every time.

Question 9

Consider this compact R expression:

result <- if (ready) if (valid) "run" else "skip" else "wait"

A maintainer wants to add braces and indentation without changing which result corresponds to each condition.

Which formatted rewrite preserves the expression's existing control flow?

  1. result <- if (ready) { if (valid) { "run" } else { "skip" } } else { "wait" } (correct answer)
  2. result <- if (ready) { if (valid) { "run" } else { "wait" } } else { "skip" }
  3. result <- if (ready && valid) { "run" } else { "skip" }
  4. result <- if (ready) { "run" } else if (valid) { "skip" } else { "wait" }
Explanation: When you see nested if-else in R without braces, the key question is: which else belongs to which if? R follows the rule that each else binds to the nearest preceding unmatched if. This is called the dangling else problem, and it's exactly what this question tests. In the original expression if (ready) if (valid) "run" else "skip" else "wait", parse it carefully: the first else "skip" binds to if (valid) (the nearest if), and the outer else "wait" binds to if (ready). So the logic is: if ready is TRUE, check valid — returning "run" or "skip"; if ready is FALSE, return "wait". Answer A correctly captures this by wrapping the inner if (valid) ... else "skip" inside the if (ready) branch, with "wait" as the outer else. That's a faithful, readable rewrite of the original. Answer B is wrong because it swaps "skip" and "wait""wait" incorrectly becomes the inner else, so an invalid-but-ready case would return "wait" instead of "skip". Answer C collapses both conditions into &&, which loses the ability to distinguish between ready-but-invalid and not-ready scenarios — it's a logic simplification, not a rewrite. Answer D restructures the chain so valid is only checked when ready is FALSE, completely inverting the intended control flow. Study tip: Whenever you encounter nested if-else without braces, explicitly trace the binding by pairing each else to its nearest unmatched if before attempting any rewrite.

Question 10

The argument timeout_ms is documented in milliseconds. The current code obtains elapsed time in seconds:

elapsed <- as.numeric(difftime(finished_at, started_at, units = "secs")) timed_out <- elapsed > timeout_ms

Which revision uses names that communicate units and implements the intended timeout comparison?

  1. elapsed_ms <- as.numeric(difftime(finished_at, started_at, units = "secs")) * 1000; timed_out <- elapsed_ms > timeout_ms (correct answer)
  2. elapsed_seconds <- as.numeric(difftime(finished_at, started_at, units = "secs")); timed_out <- elapsed_seconds > timeout_ms
  3. elapsed_ms <- as.numeric(difftime(finished_at, started_at, units = "secs")); timed_out <- elapsed_ms > timeout_ms
  4. elapsed_seconds <- as.numeric(difftime(finished_at, started_at, units = "secs")) / 1000; timed_out <- elapsed_seconds > timeout_ms
Explanation: When working with time comparisons in code, you need to ensure two things simultaneously: that variable names communicate their units clearly, and that the values being compared actually use the same units. Missing either one creates a bug — or at least misleading code. Here, timeout_ms is documented in milliseconds, so any variable you compare it against must also be in milliseconds. The original code computes elapsed time in seconds and compares it directly to a millisecond value — a unit mismatch bug. A correct revision must both rename the variable to signal its unit and convert the value appropriately. Option A does exactly this: it computes elapsed time in seconds via difftime(..., units = "secs"), multiplies by 1000 to convert to milliseconds, stores the result in elapsed_ms, and then correctly compares elapsed_ms > timeout_ms — two values now sharing the same unit. This is the right answer. Option B renames the variable to elapsed_seconds, which is honest about the unit, but then compares seconds directly to timeout_ms (milliseconds) — the unit mismatch bug remains, just made more visible by the name. Option C converts the name to elapsed_ms but forgets to multiply by 1000, so the variable claims to be milliseconds while actually holding seconds — arguably worse than the original because it's confidently wrong. Option D divides by 1000 (converting seconds to microseconds, not milliseconds) and names it elapsed_seconds, compounding both a naming error and a wrong conversion. A useful rule of thumb: whenever a function argument carries a unit suffix like _ms, _px, or _km, immediately check that every value compared to it shares that same unit — both in name and in actual computation.