Skip to content
Lesson 12 of 17

Step 1 of 5 · Reading · ~3 min

Learn

Arrays

Counting Distinct Values

"How many different words are in this line?" sounds trivial until you notice PHP has no Set type. It does not need one: an array's keys are already a hash table, and there are three builtins covering the common jobs.

GoalTool
Drop duplicate valuesarray_unique($arr)
Ask "have I seen this?" quicklythe keys — assign, then isset()
Count how often each value appearsarray_count_values($arr)

array_unique

<?php
$words = ["the", "quick", "brown", "the", "fox"];
print_r(array_unique($words));
echo count(array_unique($words)), "\n";

//> Array
//> (
//>     [0] => the
//>     [1] => quick
//>     [2] => brown
//>     [4] => fox
//> )
//> 4

Two details are doing real work there.

It keeps the original keys. The second "the" lived at index 3, so index 3 is simply gone and the result is sparse. count() does not care, but $unique[3] no longer exists. Wrap it in array_values() if you need a clean list.

It compares values as strings by default. Values are converted to text before comparison, so numbers and their string spellings collapse together:

<?php
print_r(array_unique([1, "1", 1.0, true]));

//> Array
//> (
//>     [0] => 1
//> )

Pass SORT_REGULAR as the second argument to compare with == rather than by string conversion.

Keys as a set

The other approach builds the set yourself. Every value becomes a key; whatever you store alongside it is irrelevant.

<?php
$words = ["the", "quick", "brown", "the", "fox"];
$seen = [];
foreach ($words as $w) {
    $seen[$w] = true;
}
echo count($seen), "\n";
var_dump(isset($seen["fox"]));
var_dump(isset($seen["cat"]));

//> 4
//> bool(true)
//> bool(false)

Assigning the same key twice just overwrites it, so duplicates cost nothing at all.

✗ Linear scan✓ Keys as a set
if (!in_array($w, $out)) $out[] = $w;$seen[$w] = true;
Rechecks the whole list every timeOne hash lookup
O(n2)O(n^2) overallO(n)O(n) overall

in_array inside a loop is the classic beginner shape, and it is quietly quadratic. For a handful of words nobody notices; for a log file it is the difference between instant and lunch.

array_flip($arr) builds the same structure in one call — it swaps keys and values, so the values become the keys.

Frequencies in one call

<?php
$words = ["the", "quick", "brown", "the", "fox"];
$counts = array_count_values($words);
print_r($counts);
arsort($counts);
echo array_key_first($counts), "\n";

//> Array
//> (
//>     [the] => 2
//>     [quick] => 1
//>     [brown] => 1
//>     [fox] => 1
//> )
//> the

arsort() sorts by value, descending, keeping every key attached to its own count — exactly what a "top word" report needs. Note that array_count_values only accepts int and string values; anything else is skipped with a warning.

Your exercise

Distinct Words reads one line of space-separated words and prints how many different words it contains.

The starter splits the line into $words with explode. The mistake the grader catches is counting the wrong array: the first test feeds the quick brown the, so count($words) is 4 while the expected answer is 3. Remove the duplicates before you count — count(array_unique($words)) — or build the keys-as-a-set version and count that; both are correct. The second test feeds a a a and expects 1, which rules out the shortcut of subtracting one per repeat: count($words) - 1 happens to pass the first test and fails this one. Print just the number, then a newline.

Up nextClasses and ObjectsClasses, Inheritance, Errors

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…