Красиво у каждого своё, плюс оно зависит от нюансов проекта.
При условии, что мы не знаем заранее количество ключей lvlX, я бы написал так:
// Если нужно полностью удалить элемент, внутри которого есть хотя бы один нулевой lvl
array_filter(
$a,
function (array $item): bool {
foreach ($item as $key => $value) {
if (preg_match('~^lvl\d+$~', $key) === 1 && ($value['count'] === 0 || $value['min'] === 0 || $value['max'] === 0)) {
return false;
}
}
return true;
}
);
// Если нужно удалять только сами нулевые lvl
array_map(
function (array $item): array {
return array_filter(
$item,
function ($value, string $key): bool {
return preg_match('~^lvl\d+$~', $key) !== 1 || ($value['count'] !== 0 && $value['min'] !== 0 && $value['max'] !== 0);
},
ARRAY_FILTER_USE_BOTH
);
},
$a
);
Код нужно адаптировать в зависимости от того, где там у вас реально коллекция, а где массив, но это уже тривиальная задача.
Ну и здесь предполагается, что структура каждого lvl фиксированная, то есть там всегда есть указанные ключи, чтобы не загромождать код проверками isset().
Ну а самое красивое,
на мой вкус, переделать структуру на более адекватную:
[
[
'name' => 'Alex',
'levels' => [
['index' => 1, 'count' => 5, 'min' => 12, 'max' => 5],
['index' => 2, 'count' => 0, 'min' => 5, 'max' => 7],
['index' => 3, 'count' => 18, 'min' => 0, 'max' => 8],
],
],
[
'name' => 'Igor',
'levels' => [
['index' => 1, 'count' => 5, 'min' => 12, 'max' => 5],
['index' => 2, 'count' => 0, 'min' => 5, 'max' => 7],
['index' => 3, 'count' => 18, 'min' => 0, 'max' => 8],
],
]
];
Тогда и код фильтрации будет простым и понятным.
Если же ключи lvl1, lvl2 и lvl3 фиксированные, то,
на мой вкус, лучше их захардкодить, это тоже значительно упростит жизнь в будущем, когда вы этот код откроете через пару лет и будете пытаться вспомнить что это за нагромождение из map, filter и регулярок.