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.
Need help preparing for the AP exam?
FRQs are one of the toughest parts of the AP CS exam. I offer 1-on-1 tutoring to help you work through practice problems, tighten up your responses, and build the confidence to earn full credit on exam day.
Book a tutoring session →