A frontend calls map on API results and fails because the response is no longer an array. The backend still uses a PHP array and only added a filter. The remaining numeric keys may explain the change.
1. Reproduce it with three entries
$labels = ['Draft', 'Hidden', 'Published'];
$visible = array_filter(
$labels,
static fn (string $label): bool => $label !== 'Hidden'
);
echo json_encode($visible, JSON_THROW_ON_ERROR);
// {"0":"Draft","2":"Published"}
The array_filter documentation states that retained entries keep their keys. The result therefore has keys 0 and 2, not 0 and 1.
The json_encode reference distinguishes consecutive numeric keys starting at zero from nonconsecutive keys. This example becomes a JSON object even though the PHP variable remains an array.
2. Reindex when the contract requires a list
$payload = ['items' => array_values($visible)];
echo json_encode($payload, JSON_THROW_ON_ERROR);
// {"items":["Draft","Published"]}
array_values returns values with fresh numeric indices. Here, items is deliberately a list, so discarding old keys is appropriate. With no remaining entries, items becomes [].
Avoid making clients accept both arrays and objects when the API promises a list. That can hide a server defect and force every consumer to guess the format.
3. Do not reindex lookup maps blindly
Keys in a user-ID lookup carry business meaning. Removing them loses the association between each identifier and its value:
$namesById = [41 => 'Lan', 73 => 'Minh'];
// array_values($namesById) would discard IDs 41 and 73.
Ask whether the recipient needs an ordered list or a keyed mapping. When both identity and list behavior matter, consider records with explicit id fields rather than encoding identity in array positions.
4. Make filtering intent explicit
Without a callback, array_filter removes values considered empty by PHP, including numeric zero and the string '0'. If the task is only to remove null, state that condition:
$values = [0, '0', null, 'Ready'];
$items = array_values(array_filter(
$values,
static fn ($value): bool => $value !== null
));
Filtering and list formatting are separate steps. Test them independently so a structurally correct response does not silently lose meaningful values.
5. Test shape, not just element counts
- Keeping every entry preserves a JSON array.
- Removing the first or middle entry does not create an object.
- Removing everything yields [], not null or {}.
- One remaining entry is still a list, not a scalar.
- Zero and '0' survive when allowed by the contract.
- Lookup maps retain identifiers under their own contract.
Integration tests should inspect the serialized response clients actually receive. Counting PHP array entries alone cannot detect a change from [] to {}. These examples require PHP 7.4 or later; JSON_THROW_ON_ERROR reports encoding failures through exceptions but does not validate your data contract.
A small normalization step at the serialization boundary makes an API more predictable when the distinction between lists and lookup maps is explicit.




No comments yet. Be the first to share your thoughts.