CompSci.rocks
FRQcsapa

ArrayTester: 2018 FRQ 4

A step-by-step solution to the 2018 AP CSA FRQ 4 (ArrayTester), covering extracting a column from a 2D array and verifying a Latin square by composing helper methods in Java.

Pulling a single column out of a two-dimensional array and combining three separate checks into one boolean method are the two tasks in this AP Computer Science A free-response question, built around verifying whether a grid of numbers forms a Latin square.

What This FRQ Tests

  • AP CSA units: Unit 8 (2D Array) and Unit 6 (Array)
  • Core skill: extracting a one-dimensional array from a single column of a two-dimensional array
  • Secondary skill: composing several helper methods — some already written, one you just wrote yourself — into a single larger boolean check
  • Official category: "2D Array" — always FRQ 4 on the AP CSA exam

The Setup

  • ArrayTester provides two helper methods you don't need to write (assume they work correctly):
    • boolean hasAllValues(int[] arr1, int[] arr2) — true only if every value in arr1 also appears somewhere in arr2
    • boolean containsDuplicates(int[] arr) — true if arr has any repeated values
  • You're asked to write two methods:
    • getColumn(int[][] arr2D, int c) — returns column c of arr2D as a plain 1D array
    • isLatin(int[][] square) — returns whether square is a Latin square, using getColumn, hasAllValues, and containsDuplicates
  • A square (an int[][] with equal rows and columns) is a Latin square only if all three are true:
    1. The first row has no duplicate values
    2. Every value in the first row appears in every row
    3. Every value in the first row appears in every column

Part (a): Writing getColumn(int[][] arr2D, int c)

Step-by-Step Approach

  1. The result needs one entry per row of arr2D (not per column) — its length should be arr2D.length.
  2. Create a new int[] of that size.
  3. Loop over every row index r.
  4. At each row, pull out arr2D[r][c] — row r, column c — and store it at column[r].
  5. Return the finished array.

The Code

public static int[] getColumn(int[][] arr2D, int c)
{
    int[] column = new int[arr2D.length];

    for (int r = 0; r < arr2D.length; r++)
    {
        column[r] = arr2D[r][c];
    }

    return column;
}

Why Each Piece Matters

  • new int[arr2D.length] — a column has exactly one value from every row, so its size is the number of rows, not the number of columns per row.
  • arr2D[r][c], not arr2D[c][r] — the row index always comes first in arr2D[row][col]. Swapping the two would read down the wrong direction entirely (and, unless the grid happened to be square, would likely throw an exception).
  • column[r], not column[c] — since the loop variable r tracks how far through the column you are (not the fixed column number c), the result index has to match r.

Tracing the Example

int[][] arr2D = { { 0, 1, 2 },
                   { 3, 4, 5 },
                   { 6, 7, 8 },
                   { 9, 5, 3 } };
int[] result = ArrayTester.getColumn(arr2D, 1);
r arr2D[r][1] column[r]
0 1 1
1 4 4
2 7 7
3 5 5

Final result: {1, 4, 7, 5} — matches the expected output exactly.

Common Mistakes to Avoid

  • Sizing the array with arr2D[0].length instead of arr2D.length. A column has one entry per row — using the number of columns per row instead gives the wrong size (and would even happen to work by coincidence on a square grid, hiding the bug in this exact problem's Latin-square examples).
  • Swapping the row and column indices, writing arr2D[c][r]. This reads an entirely different set of values, not the intended column.
  • Looping over arr2D[r].length instead of arr2D.length. The loop needs to visit every row, so its bound is the number of rows.

Part (b): Writing isLatin(int[][] square)

The Rule, Broken Down

The problem states you must use getColumn, hasAllValues, and containsDuplicates appropriately to receive full credit — this isn't a stylistic suggestion, it's a stated grading requirement, so each helper needs to show up doing real work:

  1. containsDuplicates(square[0]) must be false.
  2. hasAllValues(square[0], square[r]) must be true for every row r.
  3. hasAllValues(square[0], getColumn(square, c)) must be true for every column c.

Step-by-Step Approach

  1. Check the first row for duplicates immediately — if it has any, the square already fails, so return false right away.
  2. Loop over every row index and confirm square[0]'s values all appear in that row; return false the moment any row fails.
  3. Loop over every column index, extract that column with getColumn, and confirm square[0]'s values all appear in it; return false the moment any column fails.
  4. If nothing failed, every rule held — return true.

The Code

public static boolean isLatin(int[][] square)
{
    if (containsDuplicates(square[0]))
    {
        return false;
    }

    for (int r = 0; r < square.length; r++)
    {
        if (!hasAllValues(square[0], square[r]))
        {
            return false;
        }
    }

    for (int c = 0; c < square[0].length; c++)
    {
        if (!hasAllValues(square[0], getColumn(square, c)))
        {
            return false;
        }
    }

    return true;
}

Why Each Piece Matters

  • The duplicates check runs first, and returns immediately. Skipping it entirely is a real trap: a grid with a repeated value in row one can still pass both the row and column checks (a repeated value is, trivially, a value that "appears" in every row/column it needs to) — so this check can't be skipped or folded into the others.
  • square[0] is always the first argument to hasAllValues. The rule is "values from the first row appear elsewhere," not the reverse — passing the arguments in the other order would ask a different, incorrect question.
  • getColumn(square, c) reuses part (a) instead of writing a second column-reading loop. This is exactly what the problem's "must use getColumn... appropriately" requirement is checking for.
  • Early return false, rather than a boolean flag checked at the end — since the very first failure anywhere is enough to know the whole square isn't Latin, there's nothing gained by checking the rest.

Tracing the Example

Using the given Latin square:

1 2 3
2 3 1
3 1 2
  • containsDuplicates({1, 2, 3})false → continue.
  • Row check: row {1,2,3}, row {2,3,1}, row {3,1,2}hasAllValues({1,2,3}, ...) is true for all three.
  • Column check: column 0 is {1,2,3}, column 1 is {2,3,1}, column 2 is {3,1,2}hasAllValues({1,2,3}, ...) is true for all three.
  • No check ever failed → returns true. Matches the expected "Latin square."

Using the first non-Latin example:

1 2 1
2 1 1
1 1 2
  • containsDuplicates({1, 2, 1})true (the value 1 appears twice) → isLatin returns false immediately, without ever checking rows or columns. Matches the stated reason: "the first row contains duplicate values."

Using the second non-Latin example:

1 2 3
3 1 2
7 8 9
  • containsDuplicates({1,2,3})false → continue.
  • Row check: row {3,1,2} passes, but row {7,8,9} fails — none of 1, 2, or 3 appear in it — so isLatin returns false here. Matches the stated reason: "the elements of the first row do not all appear in the third row."

Using the third non-Latin example:

1 2
1 2
  • containsDuplicates({1,2})false → continue.
  • Row check: both rows are {1,2}, so hasAllValues({1,2}, {1,2}) passes both times — the row check alone would incorrectly call this a Latin square.
  • Column check: column 0 is {1,1}. hasAllValues({1,2}, {1,1}) asks whether 1 and 2 both appear in {1,1}1 does, but 2 never does, so this fails, and isLatin returns false here. Matches the stated reason: "the elements of the first row do not all appear in either column" — and shows exactly why the column check can't be skipped even when every row happens to pass.

Common Mistakes to Avoid

  • Skipping the containsDuplicates check. As the third example above shows for rows, a duplicate-filled first row can still pass every row (or column) check on its own — this is the one rule that genuinely needs its own dedicated test.
  • Passing hasAllValues's arguments in the wrong order (e.g., hasAllValues(square[r], square[0])). The rule is specifically about the first row's values showing up elsewhere, not the other way around.
  • Writing a second, separate loop to manually pull out each column instead of calling getColumn — this both duplicates code and ignores the problem's explicit "must use getColumn" requirement.
  • Using a boolean flag and checking it only at the very end, rather than returning false immediately on the first failure — not wrong, exactly, but it means every row and column gets checked even after the answer is already known.

Key Takeaways

  • A 2D array's column has one entry per row — sizing or looping a column-related array by arr2D[0].length (row length) instead of arr2D.length (row count) is one of the most common 2D array bugs.
  • When a problem gives you helper methods and requires you to use them, that's a real signal about which method should call which — look for how each helper's inputs and outputs are meant to compose.
  • Returning false (or true) the moment a condition is known, rather than finishing every check first, is usually simpler and avoids unnecessary work.

Related FRQs