PHP

Gestion d’occurences en PHP

Avec des if, puis sans if, puis array_key_exists, puis array_count_values et une belle classe pour finir

→ Corrigé du Challenge : Team Pokemon

V1, avec des if

$nbFeu = 0;
$nbEau = 0;
$nbHerbe = 0;
$nbRare = 0;

foreach ($types as $type) {

	if ($type === 'Feu') {
		$nbFeu++;
		continue;
	}

	if ($type === 'Eau') {
		$nbEau++;
		continue;
	}

	if ($type === 'Herbe') {
		$nbHerbe++;
		continue;
	}

	$nbRare++;
}

$reponse = min([$nbFeu, $nbEau, $nbHerbe, $nbRare]);

Explications

Je commence par initialiser 4 compteurs, tous à zéro, pour compter les types que je rencontre.

Je parcours ensuite chaque type. Dès que je trouve le type que je recherche, j’utilise un continue pour passer à l’itération suivante. Cela permet notamment ici d’éviter les if/elseif/elseif… successifs. Si je ne suis ni Feu, ni Eau, ni Herbe, c’est que je suis Rare (pas besoin de test supplémentaire donc ici).

J’utilise pour finir la fonction min pour retourner le minimum de chaque type/groupe qui sera le type/groupe discriminant pour construire mes groupes.

V2, plus de if

$nbFeu = 0;
$nbEau = 0;
$nbHerbe = 0;

foreach ($types as $type) {
	$nbFeu += (int) ($type === 'Feu');
	$nbEau += (int) ($type === 'Eau');
	$nbHerbe += (int) ($type === 'Herbe');
}

$nbRare = count($types) - $nbFeu - $nbEau - $nbHerbe;

$reponse = min([$nbFeu, $nbEau, $nbHerbe, $nbRare]);

Explications

Dans cette version, j’ai remplacé mes if par des incréments particuliers… En fait, je vais transformer le résultat de mon test en entier. Donc true => 1 et false => 0. De cette manière, si le test n’est pas vérifié, j’ajoute 0 (donc je ne fais rien). S’il est vérifié, j’ajoute 1.

Par contre, je ne peux pas utiliser la même chose pour compte les Rares. Du coup, je prends tous les types, moins ceux calculés dans le foreach.

Pas de changement pour l’utilisation de min.

V3, avec array_key_exists

$groupes = ['Feu' => 0, 'Eau' => 0, 'Herbe' => 0, 'Rare' => 0];

foreach ($types as $type) {
	if (array_key_exists($type, $groupes)) {
		$groupes[$type]++;
		continue;
	}

	$groupes['Rare']++;
}

echo min($groupes);

Explications

Cette fois-ci, au lieu d’initialiser 4 compteurs avec 4 variables, je crée un tableau qui contient mes compteurs. Mes clés portent les noms exacts des types « Feu », « Eau » et « Herbe ». Et une clé « Rare ».

Au parcours des types, je vais vérifier si le type courant a une clé correspondante dans $groupes. Si c’est le cas, j’incrémente le compteur correspondant de 1, et je passe à l’itération suivante avec le continue. Si ce n’est pas le cas, c’est que je suis dans le cas de « Rare » et c’est donc ce compteur que j’incrémente de 1.

Pour finir, il me suffit d’appliquer min directement sur $groupes. Ce sont les valeurs qui seront analysées, et non les clés. Donc peu importe comment les valeurs du tableau sont indexées.

V4, avec array_count_values

$values = array_count_values($types);

$reponse = min(
	$values['Feu'],
	$values['Eau'],
	$values['Herbe'],
	count($types) - $values['Feu'] - $values['Eau'] - $values['Herbe']
);

Explications

array_count_values est une fonction native de PHP, qui fait (grosso modo) exactement la même chose que la V3. Je vais récupérer un tableau avec comme clés les différents éléments de mon (premier) tableau, et comme valeurs le nombre d’occurences de chacun de ces éléments.

ATTENTION : ce code n’est pas 100% correct ! En effet, si je n’ai pas de type « Eau », array_count_values ne saura pas créer « Eau » => 0. Il faudrait donc tester l’existence de chaque clé avant de vouloir l’utiliser…

V5, avec une fonction

function countType(string|array $searchType, array $types): int
{
	$searchType = (array) $searchType;

	$count = 0;
	foreach ($types as $type) {
		$count += (int) in_array($type, $searchType);
	}

	return $count;
}

$reponse = min(
	countType('Feu', $types),
	countType('Eau', $types),
	countType('Herbe', $types),
	countType(['Insecte', 'Psychique', 'Air', 'Glace', 'Poison'], $types)

Explications

La fonction countType permettra de compter le nombre d’occurrence d’un ou plusieurs types dans un tableau de type.

Cette ligne permet de transformer un élément seul en un tableau contenant ce seul élément. Un tableau sera inchangé.

Je parcours chaque type et mon compteur s’incrémente comme dans V2. J’utilise ici in_array, c’est pourquoi je tenais à avoir toujours pour $searchType.

Pour obtenir la réponse, j’applique donc ma fonction countType, toujours sur $types, mais en passant soit un type seul, soit un tableau de types recherchés, pour les rares.

Remarque : l’union de typage dans la déclaration d’une fonction, ici string|array, est disponible à partir de PHP 8.0

V6, avec une classe

final class POKEMONS
{
	private const TYPES_RARES = ['Air', 'Poison', 'Glace', 'Psychique', 'Insecte'];
	private const TYPES_CLASSIQUES = ['Feu', 'Eau', 'Herbe'];
	private array $types;
	private array $countTypes;

	public function __construct(array $types)
	{
		$this->types = $types;
		$this->countTypes = array_count_values($types);
	}

	private function countType(string $type): int
	{	
		return $this->countTypes[$type] ?? 0;
	}

	public function nbGroups(): int
	{
		return min(
			array_map([$this, 'countType'], self::TYPES_CLASSIQUES)
			+ 
			[array_sum(array_map([$this, 'countType'], self::TYPES_RARES))]
		);
	}
}

$reponse = (new POKEMONS($types))->nbGroups();

Explications

Je construis 2 constantes qui vont contenir la liste des types, selon qu’ils sont classiques ou rares.

À la construction de ma classe, j’applique tout de suite array_count_values sur $types.

Par contre, j’ai une méthode countType qui va vérifier que ma clé existe, et retournera 0 si elle n’existe pas. Comme ça, plus d’erreur comme expliqué dans V4. L’écriture « ?? » remplace « if(isset(…)) … ».Détails sur l’opérateur Null coalescent.

Enfin, dans nbGroups, je fais plusieurs choses…

Avec le premier array_map, je vais appliquer « countType » à chaque élément des types classiques. Je vais donc récupérer un tableau du genre [4, 3, 4] qui contient les occurences de chaque type classique.

Pour les rares, j’applique par-dessus un array_sum pour faire la somme de tous les pokemons rares. Je n’aurais donc qu’une seule valeur, disons 5 pour l’exemple. Que je remets dans un tableau => [5].

Ensuite, le + me permet de faire [4, 3, 4] + [5]. Dans ce cas précis, cela me donne comme résultat [4, 3, 4, 5]. C’est sur ce tableau final que la fonction min est appliquée.


Qui a codé ce superbe contenu ?


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.