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
ArrayTesterprovides two helper methods you don't need to write (assume they work correctly):boolean hasAllValues(int[] arr1, int[] arr2)— true only if every value inarr1also appears somewhere inarr2boolean containsDuplicates(int[] arr)— true ifarrhas any repeated values
- You're asked to write two methods:
getColumn(int[][] arr2D, int c)— returns columncofarr2Das a plain 1D arrayisLatin(int[][] square)— returns whethersquareis a Latin square, usinggetColumn,hasAllValues, andcontainsDuplicates
- A square (an
int[][]with equal rows and columns) is a Latin square only if all three are true:- The first row has no duplicate values
- Every value in the first row appears in every row
- Every value in the first row appears in every column
Part (a): Writing getColumn(int[][] arr2D, int c)
Step-by-Step Approach
- The result needs one entry per row of
arr2D(not per column) — its length should bearr2D.length. - Create a new
int[]of that size. - Loop over every row index
r. - At each row, pull out
arr2D[r][c]— rowr, columnc— and store it atcolumn[r]. - 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], notarr2D[c][r]— the row index always comes first inarr2D[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], notcolumn[c]— since the loop variablertracks how far through the column you are (not the fixed column numberc), the result index has to matchr.
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].lengthinstead ofarr2D.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].lengthinstead ofarr2D.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:
containsDuplicates(square[0])must befalse.hasAllValues(square[0], square[r])must betruefor every rowr.hasAllValues(square[0], getColumn(square, c))must betruefor every columnc.
Step-by-Step Approach
- Check the first row for duplicates immediately — if it has any, the square already fails, so return
falseright away. - Loop over every row index and confirm
square[0]'s values all appear in that row; returnfalsethe moment any row fails. - Loop over every column index, extract that column with
getColumn, and confirmsquare[0]'s values all appear in it; returnfalsethe moment any column fails. - 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 tohasAllValues. 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 usegetColumn... 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}, ...)istruefor 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}, ...)istruefor 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 value1appears twice) →isLatinreturnsfalseimmediately, 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 of1,2, or3appear in it — soisLatinreturnsfalsehere. 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}, sohasAllValues({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 whether1and2both appear in{1,1}—1does, but2never does, so this fails, andisLatinreturnsfalsehere. 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
containsDuplicatescheck. 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 usegetColumn" requirement. - Using a boolean flag and checking it only at the very end, rather than returning
falseimmediately 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 ofarr2D.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(ortrue) the moment a condition is known, rather than finishing every check first, is usually simpler and avoids unnecessary work.