How it works
When an array is sorted, equal values are adjacent. A read pointer can scan the input while a write pointer marks the next position in a compact unique prefix. Each new value is copied forward once, and repeated adjacent values are skipped. This avoids allocating another full result array.
The sample transforms the meaningful prefix into one, two, three and returns length three. The underlying array still has five physical slots. Code must use the returned length rather than printing the entire array and interpreting the leftover tail as part of the result. In-place compaction changes the logical view, not the fixed array length.
The invariant is that positions before write contain the distinct values encountered so far, in order. Comparing the next read value with values[write - 1] checks the most recent retained value. The empty case is handled before that expression could access an invalid index.
Sorted input is a real precondition. On an unsorted array, separated duplicates will not be removed by this algorithm. If the application cannot guarantee ordering, either validate or establish it, or choose a hash-based distinct operation with its own ordering and storage policy. Do not silently advertise this as a general duplicate remover.
Practice with an empty array, a single element, all equal elements, and an already unique sorted array. Record both returned length and meaningful prefix. Then try an unsorted input deliberately and explain which assumption fails without changing the algorithm's stated contract.
Finally, compare this mutating operation with a function that returns a new array. Mutation saves result storage but affects every caller sharing the input array. Choose according to ownership: a borrowed array may need copying before compaction. Correctness includes both the unique-prefix invariant and the promise about which component is allowed to modify the original storage.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
int[] values = { 1, 1, 2, 2, 3 };
int length = Compact(values);
Console.WriteLine(length);
Console.WriteLine(string.Join(",", values.Take(length)));
static int Compact(int[] values) {
if (values.Length == 0) return 0;
int write = 1;
for (int read = 1; read < values.Length; read++) {
if (values[read] != values[write - 1]) values[write++] = values[read];
}
return write;
}
Expected output
3 1,2,3
Only the returned prefix is the result; the unused physical tail is unspecified.