nur-sery/nur_src/A.php

1568 lines
50 KiB
PHP
Raw Normal View History

2023-12-03 22:10:18 +04:00
<?php
namespace nur;
use ArrayAccess;
use nur\b\coll\BaseArray;
use nur\b\coll\Flattener;
use nur\b\coll\IArray;
use Traversable;
/**
* Class A: méthodes utilitaires pour gérer les array
*/
class A {
static final function with($array): array { return SL::with($array); }
static final function withn($array): ?array { return SL::withn($array); }
/**
* tester si $array est un tableau de type array, BaseArray ou Traversable.
* tous ces objets peuvent être transformés en array par {@link with()}
*
* pour être sûr de pouvoir les parcourir en lecture seule, il faut les
* transformer en array avec {@link with()}
*/
static final function is_array($array): bool {
if ($array === null || $array === false) return false;
return is_iterable($array) || $array instanceof BaseArray;
}
static final function ensure_array(&$array): bool { return SL::ensure_array($array); }
static final function ensure_narray(&$array): bool { return SL::ensure_narray($array); }
/**
* retourner une référence permettant de modifier $array en tant qu'array de
* préférence ou en tant qu'instance de ArrayAccess au pire.
*/
static final function &ensure_access(&$array) {
if (is_array($array)) return $array;
if ($array instanceof IArray) return $array->array();
if ($array instanceof ArrayAccess) return $array;
if ($array instanceof Traversable) $array = iterator_to_array($array);
elseif ($array === null || $array === false) $array = [];
else $array = [$array];
return $array;
}
/**
* s'assurer que $array est un tableau de $size éléments, en complétant avec
* des occurrences de $default si nécessaire
*
* @return bool true si le tableau a été modifié, false sinon
*/
static final function ensure_size(?array &$array, int $size, $default=null): bool {
$modified = false;
if ($array === null) {
$array = [];
$modified = true;
}
if ($size < 0) return $modified;
$count = count($array);
if ($count == $size) return $modified;
if ($count < $size) {
# agrandir le tableau
while ($count++ < $size) {
$array[] = $default;
}
return true;
}
# rétrécir le tableau
$tmparray = [];
foreach ($array as $key => $value) {
if ($size-- == 0) break;
$tmparray[$key] = $value;
}
$array = $tmparray;
return true;
}
/** tester si $array contient la clé $key */
static final function has(?array $array, $key): bool {
return $array !== null && array_key_exists($key, $array);
}
/** retourner $array[$key] ou $default si la clé n'existe pas */
static final function get(?array $array, $key, $default=null) {
if ($array === null) return $default;
elseif (array_key_exists($key, $array)) return $array[$key];
else return $default;
}
/** s'assurer que $array est un array puis spécifier $array[$key] */
static final function set(&$array, $key, $value): void {
self::ensure_array($array);
if ($key === null) $array[] = $value;
else $array[$key] = $value;
}
/** s'assurer que $array est un array puis supprimer $array[$key] */
static final function del(&$array, $key): void {
if ($array === null || $array === false) return;
self::ensure_array($array);
unset($array[$key]);
}
/** retourner le nombre d'éléments de $array */
static final function count(?array $array): int {
return $array === null? 0: count($array);
}
/** retourner la liste des clés de $array */
static final function keys(?array $array): array {
return $array === null? []: array_keys($array);
}
static final function join(?array $array, string $sep=" "): string {
return $array === null? "": implode($sep, $array);
}
#############################################################################
/**
* tester si $array est un tableau séquentiel.
*
* NB: un tableau vide est séquentiel
*/
static final function is_seq($array): bool {
if (!is_array($array)) return false;
$count = count($array);
if ($count == 0) return true;
return array_keys($array) === range(0, $count - 1);
}
/**
* tester si $array est un tableau associatif.
*
* NB: un tableau vide est associatif
*/
static final function is_assoc($array): bool {
if (!is_array($array)) return false;
$count = count($array);
if ($count == 0) return true;
return array_keys($array) !== range(0, $count - 1);
}
#############################################################################
/**
* s'assurer que $array est un array puis y fusionner tous les autres tableaux
* avec array_merge(). les clés numériques sont réordonnées, et les clés du
* tableau destination sont écrasées par les clés correspondantes des tableaux
* entrants. par exemple:
* ~~~
* $a = [10 => "a", 15 => "b", "x" => "y"];
* $b = [ 15 => "c", 20 => "d", "x" => "z"];
* A::merge($a, $b);
* # $a vaut maintenant [0 => "a", 1 => "b", "x" => "z", 2 => "c", 3 => "d"]
* ~~~
*
* parmi les tableaux de $arrays, ignorer les occurrences de null et false
*
* NB: dans $arrays, les valeurs scalaires sont traitées comme un singleton
* [$value]. elles sont donc simplement ajoutées à $array comme avec la
* commande "$array[] = $value;"
*/
static final function merge(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$merges = [$array];
foreach ($arrays as $merge) {
if ($merge === null || $merge === false) continue;
$merges[] = self::with($merge);
}
$array = array_merge(...$merges);
}
}
/**
* s'assurer que $array est un array puis y fusionner tous les autres tableaux
* comme avec {@link merge()} mais sans réordonner les clés numériques. Cela
* ressemble au comportement de l'opérateur union de PHP, mais la différence
* est que les clés du tableau destination sont écrasées par les clés
* correspondantes des tableaux entrants. par exemple:
* ~~~
* $a = [10 => "a", 15 => "b", "x" => "y"];
* $b = [ 15 => "c", 20 => "d", "x" => "z"];
* A::merge2($a, $b);
* # $a vaut maintenant [10 => "a", 15 => "c", "x" => "z", 20 => "d"]
* ~~~
*
* parmi les tableaux de $arrays, ignorer les occurrences de null et false
*
* NB: dans $arrays, les valeurs scalaires sont traitées comme un singleton
* [$value]. elles écrasent donc la valeur à la clé '0' si celle-ci existe
* déjà dans le tableau destination
*/
static final function merge2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $merge) {
if ($merge === null || $merge === false) continue;
foreach (self::with($merge) as $key => $value) {
$array[$key] = $value;
}
}
}
/**
* s'assurer que $array est un array puis y fusionner tous les autres tableaux
* un peu comme avec {@link merge()} mais en ne réordonnant *que* les clés
* séquentielles. par exemple:
* ~~~
* $a = ["1st", "2nd", 10 => "a", 15 => "b", "x" => "y"];
* $b = [ "3rd", "4th", 15 => "c", 20 => "d", "x" => "z"];
* A::merge3($a, $b);
* # $a vaut maintenant ["1st", "2nd", 10 => "a", 15 => "c", "x" => "z", "3rd", "4th", 20 => "d"]
* ~~~
*
* parmi les tableaux de $arrays, ignorer les occurrences de null et false
*/
static final function merge3(&$array, ...$arrays): void {
self::ensure_array($array);
$desti = 0;
foreach ($array as $key => $value) {
if ($key === $desti) {
$desti++;
}
}
foreach ($arrays as $merge) {
$srci = 0;
if ($merge === null || $merge === false) continue;
foreach (self::with($merge) as $key => $value) {
if ($key === $srci) {
$srci++;
$array[$desti++] = $value;
} else {
$array[$key] = $value;
}
}
}
}
/**
* comme {@link merge()} mais dans chacun des tableaux sources, ceux de
* $arrays, ignorer les valeurs null.
*/
static final function merge_nn(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$merges = [$array];
foreach ($arrays as $tmp) {
if ($tmp === false || $tmp === null) continue;
$merge = [];
foreach (self::with($tmp) as $key => $value) {
if ($value === null) continue;
$merge[$key] = $value;
}
$merges[] = $merge;
}
$array = array_merge(...$merges);
}
}
/**
* comme {@link merge2()} mais dans chacun des tableaux sources, ceux de
* $arrays, ignorer les valeurs null.
*/
static final function merge_nn2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $merge) {
if ($merge === false || $merge === null) continue;
foreach (self::with($merge) as $key => $value) {
if ($value === null) continue;
$array[$key] = $value;
}
}
}
/**
* comme {@link merge()} mais dans chacun des tableaux sources, ceux de
* $arrays, ignorer les valeurs null et false.
*/
static final function merge_nz(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$merges = [$array];
foreach ($arrays as $tmp) {
if ($tmp === false || $tmp === null) continue;
$merge = [];
foreach (self::with($tmp) as $key => $value) {
if ($value === null || $value === false) continue;
$merge[$key] = $value;
}
$merges[] = $merge;
}
$array = array_merge(...$merges);
}
}
/**
* comme {@link merge2()} mais dans chacun des tableaux sources, ceux de
* $arrays, ignorer les valeurs null et false.
*/
static final function merge_nz2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $merge) {
if ($merge === false || $merge === null) continue;
foreach (self::with($merge) as $key => $value) {
if ($value === null || $value === false) continue;
$array[$key] = $value;
}
}
}
/**
* comme {@link merge()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas
*/
static final function update_nx(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$updates = [$array];
foreach ($arrays as $tmp) {
if ($tmp === false || $tmp === null) continue;
$update = [];
foreach (self::with($tmp) as $key => $value) {
if (!array_key_exists($key, $array)) {
$update[$key] = $value;
}
}
$updates[] = $update;
}
$array = array_merge(...$updates);
}
}
/**
* comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas
*
* NB: il s'agit de la définition du l'opérateur union. l'implémentation fait
* d'ailleurs usage de cet opérateur
*/
static final function update_nx2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $update) {
if ($update === null || $update === false) continue;
$array += self::with($update);
}
}
/**
* comme {@link merge()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas ou, si elle y existe, que la valeur y est null
*/
static final function update_n(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$updates = [$array];
foreach ($arrays as $tmp) {
if ($tmp === false || $tmp === null) continue;
$update = [];
foreach (self::with($tmp) as $key => $value) {
if (!array_key_exists($key, $array)) {
$update[$key] = $value;
} elseif ($array[$key] === null) {
$update[$key] = $value;
}
}
$updates[] = $update;
}
$array = array_merge(...$updates);
}
}
/**
* comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas ou, si elle y existe, que la valeur y est null
*/
static final function update_n2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $update) {
if ($update === false || $update === null) continue;
foreach (self::with($update) as $key => $value) {
if (!array_key_exists($key, $array)) {
$array[$key] = $value;
} elseif ($array[$key] === null) {
$array[$key] = $value;
}
}
}
}
/**
* comme {@link merge()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas ou, si elle y existe, que la valeur y est null ou false
*/
static final function update_z(&$array, ...$arrays): void {
self::ensure_array($array);
if ($arrays) {
$updates = [$array];
foreach ($arrays as $tmp) {
if ($tmp === false || $tmp === null) continue;
$update = [];
foreach (self::with($tmp) as $key => $value) {
if (!array_key_exists($key, $array)) {
$update[$key] = $value;
} else {
$rvalue = $array[$key];
if ($rvalue === null || $rvalue === false) {
$update[$key] = $value;
}
}
}
$updates[] = $update;
}
$array = array_merge(...$updates);
}
}
/**
* comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
* $arrays, ne fusionner les valeurs dans la destination $array que si la clé
* n'y existe pas ou, si elle y existe, que la valeur y est null ou false
*/
static final function update_z2(&$array, ...$arrays): void {
self::ensure_array($array);
foreach ($arrays as $tomerge) {
if ($tomerge === false || $tomerge === null) continue;
foreach (self::with($tomerge) as $key => $value) {
if (!array_key_exists($key, $array)) {
$array[$key] = $value;
} else {
$rvalue = $array[$key];
if ($rvalue === null || $rvalue === false) {
$array[$key] = $value;
}
}
}
}
}
#############################################################################
## get
/** obtenir le premier élément du tableau */
static final function first(?array $array, $default=null) {
if (!$array) return $default;
else return $array[array_key_first($array)];
}
/** obtenir le second élément du tableau */
static final function second(?array $array, $default=null) {
if (!$array) return $default;
$first = true;
foreach ($array as $value) {
if ($first) $first = false;
else return $value;
}
return $default;
}
/**
* obtenir le n-ième élément du tableau, en commençant à 0 (i.e 0=premier,
* 1=second, etc.)
*/
static final function nth(?array $array, int $index, $default=null) {
if (!$array) return $default;
$i = 0;
foreach ($array as $value) {
if ($index == $i) return $value;
$i++;
}
return $default;
}
/** obtenir la première clé du tableau */
static final function first_key(?array $array, $default_key=null) {
if (!$array) return $default_key;
else return array_key_first($array);
}
/** obtenir la seconde clé du tableau */
static final function second_key(?array $array, $default_key=null) {
if (!$array) return $default_key;
$first = true;
foreach (array_keys($array) as $key) {
if ($first) $first = false;
else return $key;
}
return $default_key;
}
/**
* obtenir la clé du n-ième élément du tableau, en commençant à 0 (i.e
* 0=premier, 1=second, etc.)
*/
static final function nth_key(?array $array, int $index, $default_key=null) {
if (!$array) return $default_key;
$i = 0;
foreach (array_keys($array) as $key) {
if ($index == $i) return $key;
$i++;
}
return $default_key;
}
/** obtenir le dernier élément du tableau */
static final function last(?array $array, $default=null) {
if (!$array) return $default;
else return $array[array_key_last($array)];
}
/** obtenir la dernière clé du tableau */
static final function last_key(?array $array, $default_key=null) {
if (!$array) return $default_key;
else return array_key_last($array);
}
/**
* si $array est un tableau séquentiel avec un seul élément, retourner cet élément.
* si c'est un tableau vide ou null retourner $default
* sinon retourner le tableau inchangé
*/
static final function one_or_array(?array $array, $default=null) {
if (!$array) {
return $default;
} elseif (count($array) == 1 && array_key_exists(0, $array)) {
return $array[0];
} else {
return $array;
}
}
/**
* retourner un tableau avec pour chaque tableau $array de $arrays, la valeur
* de get($array, $key, $default)
*/
static final function each_get(?array $arrays, $key, $default=null): array {
$values = [];
foreach ($arrays as $index => $array) {
$values[$index] = self::get($array, $key, $default);
}
return $values;
}
#############################################################################
## set
/**
* Mettre à jour la clé $key avec $value si $value !== null
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function set_nn(&$array, $key, $value) {
if ($value !== null) {
self::ensure_array($array);
self::set($array, $key, $value);
return $value;
} else {
return self::get($array, $key);
}
}
/**
* Mettre à jour la clé $key avec $array[$key_indirect] si cette valeur ne
* vaut pas null
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function set_nn_indirect(&$array, $key, $key_indirect) {
self::ensure_narray($array);
$value = self::get($array, $key_indirect);
return self::set_nn($array, $key, $value);
}
/**
* Mettre à jour la clé $key avec $value si $value ne vaut ni null ni false
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function set_nz(&$array, $key, $value) {
if ($value !== false && $value !== null) {
self::set($array, $key, $value);
return $value;
} else {
return self::get($array, $key);
}
}
/**
* Mettre à jour la clé $key avec $array[$key_indirect] si cette valeur ne
* vaut ni null ni false
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function set_nz_indirect(&$array, $key, $key_indirect) {
self::ensure_narray($array);
$value = self::get($array, $key_indirect);
return self::set_nz($array, $key, $value);
}
/**
* Ajouter la valeur avec la clé spécifiée si elle n'existe pas déjà
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function replace_nx(&$array, $key, $value) {
if (!is_array($array) || !array_key_exists($key, $array)) {
self::set($array, $key, $value);
return $value;
} else {
return self::get($array, $key);
}
}
/**
* Mettre à jour la clé $key avec $value si la clé $key n'existe pas ou si sa
* valeur actuelle est null.
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function replace_n(&$array, $key, $value) {
self::ensure_narray($array);
$rvalue = self::get($array, $key);
if ($rvalue === null) {
self::set($array, $key, $value);
$rvalue = $value;
}
return $rvalue;
}
/**
* Mettre à jour la clé $key avec $array[$key_indirect] si cette clé n'existe
* pas ou si sa valeur actuelle est null.
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function replace_n_indirect(&$array, $key, $key_indirect) {
self::ensure_narray($array);
$rvalue = self::get($array, $key);
if ($rvalue === null) {
$value = self::get($array, $key_indirect);
self::set($array, $key, $value);
$rvalue = $value;
}
return $rvalue;
}
/**
* Mettre à jour la clé $key avec $value si la clé $key n'existe pas ou si sa
* valeur actuelle est null ou false.
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function replace_z(&$array, $key, $value) {
self::ensure_narray($array);
$rvalue = self::get($array, $key);
if ($rvalue === false || $rvalue === null) {
self::set($array, $key, $value);
$rvalue = $value;
}
return $rvalue;
}
/**
* Mettre à jour la clé $key avec $array[$key_indirect] si cette clé n'existe
* pas ou si sa valeur actuelle est null ou false.
*
* Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
* non)
*/
static final function replace_z_indirect(&$array, $key, $key_indirect) {
self::ensure_narray($array);
$rvalue = self::get($array, $key);
if ($rvalue === false || $rvalue === null) {
$value = self::get($array, $key_indirect);
self::set($array, $key, $value);
$rvalue = $value;
}
return $rvalue;
}
/**
* Ajouter $value à la fin de $array
*
* Retourner la valeur ajoutée
*/
static final function append(&$array, $value) {
self::ensure_array($array);
$array[] = $value;
return $value;
}
/**
* Si $value n'est pas null, ajouter $value à la fin de $array
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
*/
static final function append_nn(&$array, $value) {
if ($value !== null) {
self::ensure_array($array);
$array[] = $value;
return $value;
} else {
return null;
}
}
/**
* Si $value ne vaut ni null ni false, ajouter $value à la fin de $array
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
*/
static final function append_nz(&$array, $value) {
if (base::nz($value)) {
self::ensure_array($array);
$array[] = $value;
return $value;
} else {
return null;
}
}
/**
* Insérer $value au début de $array
*
* Retourner la valeur insérée
*/
static final function prepend(&$array, $value) {
self::ensure_array($array);
array_unshift($array, $value);
return $value;
}
/**
* Si $value n'est pas null, insérer $value au début de $array
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
*/
static final function prepend_nn(&$array, $value) {
if ($value !== null) {
self::ensure_array($array);
array_unshift($array, $value);
return $value;
} else {
return null;
}
}
/**
* Si $value ne vaut ni null ni false, insérer $value au début de $array
*
* si aucune modification ne doit se faire, $array n'est pas modifié
*
* Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
*/
static final function prepend_nz(&$array, $value) {
if (base::nz($value)) {
self::ensure_array($array);
array_unshift($array, $value);
return $value;
} else {
return null;
}
}
/**
* dans le tableau séquentiel $array, insérer $value à la position $index et
* décaler toutes les autres clés
*
* Retourner la valeur insérée
*/
static final function insert(&$array, $index, $value) {
self::ensure_array($array);
$count = count($array);
if ($count == 0 || $index >= $count) {
$array[] = $value;
} else {
while ($index < 0) $index += $count;
if ($index == 0) $prefix = [];
else $prefix = array_slice($array, 0, $index);
$suffix = array_slice($array, $index);
$array = array_merge($prefix, [$value], $suffix);
}
return $value;
}
/** Ajouter une valeur dans le tableau, pour utilisation avec pop() */
static final function push(&$array, $value): void {
self::ensure_array($array);
$array[] = $value;
}
/**
* enlever la dernière valeur ajoutée dans le tableau avec push() et la
* retourner
*/
static final function pop(&$array, $default=null) {
self::ensure_array($array);
$count = count($array);
if ($count > 0) {
$value = array_pop($array);
} else {
$value = $default;
}
return $value;
}
#############################################################################
## del
static final function del_value(&$array, $value, int $max_count=1, bool $strict=false): int {
if ($array === null || $array === false) return 0;
self::ensure_array($array);
$count = 0;
$rekey = null;
while ($max_count <= 0 || $count < $max_count) {
$key = array_search($value, $array, $strict);
if ($key === false) break;
# s'il faut supprimer des clés, vérifier d'abord si c'est un tableau
# séquentiel, afin de refaire la numérotation le cas échéant
if ($rekey === null) $rekey = self::is_seq($array);
unset($array[$key]);
$count++;
}
if ($rekey) $array = array_values($array);
return $count;
}
static final function del_first_key(&$array, int $max_count=1): int {
if ($array === null || $array === false) return 0;
self::ensure_array($array);
$count = 0;
$rekey = null;
while ($max_count <= 0 || $count < $max_count) {
$key = array_key_first($array);
if ($key === null) break;
# s'il faut supprimer des clés, vérifier d'abord si c'est un tableau
# séquentiel, afin de refaire la numérotation le cas échéant
if ($rekey === null) $rekey = self::is_seq($array);
unset($array[$key]);
$count++;
}
if ($rekey) $array = array_values($array);
return $count;
}
static final function del_last_key(&$array, int $max_count=1): int {
if ($array === null || $array === false) return 0;
self::ensure_array($array);
$count = 0;
while ($max_count <= 0 || $count < $max_count) {
$key = array_key_last($array);
if ($key === null) break;
unset($array[$key]);
$count++;
}
return $count;
}
static final function del_keys(&$array, ...$keys): void {
if ($array === null || $array === false) return;
self::ensure_array($array);
foreach ($keys as $key) {
unset($array[$key]);
}
}
#############################################################################
## getdel
/**
* obtenir la valeur correspondant à la clé $key, ou $default si elle n'est
* pas trouvée. puis supprimer la clé du tableau
*/
static final function getdel(&$array, $key, $default=null) {
self::ensure_array($array);
$value = self::get($array, $key, $default);
unset($array[$key]);
return $value;
}
/**
* obtenir les valeurs correspondantes aux clés $keys sous forme de tableau
* séquentiel. supprimer les clés du tableau
*/
static final function getdels(&$array, ?array $keys): array {
self::ensure_array($array);
$values = [];
if ($keys !== null) {
foreach ($keys as $key) {
$values[] = self::get($array, $key);
unset($array[$key]);
}
}
return $values;
}
#############################################################################
## Chemins de clé
/**
* vérifier que le chemin $keys fourni sous forme de tableau existe dans le
* tableau $array
*
* si $keys est vide ou null, retourner true
*/
static final function _phas(?array $array, ?array $keys): bool {
$first = true;
foreach($keys as $key) {
if ($key === "" && $first) {
# une chaine vide en première position est ignorée
continue;
} elseif (is_array($array)) {
if (!array_key_exists($key, $array)) return false;
$array = $array[$key];
} elseif ($array instanceof ArrayAccess) {
if (!$array->offsetExists($key)) return false;
$array = $array->offsetGet($key);
} else {
return false;
}
$first = false;
}
return true;
}
/**
* Vérifier que le chemin $keys qui est de la forme key[.keys...] existe
* dans le tableau $array
*
* si $keys === null, retourner true
*/
static final function phas_s(?array $array, ?string $keys): bool {
if ($keys === null) return true;
$keys = explode(".", $keys);
return self::_phas($array, $keys);
}
/**
* Vérifier que le chemin $keys fourni sous forme de tableau existe dans le
* tableau $array
*
* si $keys est nul ou vide, retourner true
*/
static final function phas_a(?array $array, ?array $keys): bool {
if (!$keys) return true;
$keys = implode(".", $keys);
$keys = explode(".", $keys);
return self::_phas($array, $keys);
}
/** vérifier que le chemin $keys existe dans le tableau $array */
static final function phas($array, $keys): bool {
if ($keys === null) return true;
elseif (is_array($keys)) return self::phas_a($array, $keys);
else return self::phas_s($array, strval($keys));
}
/**
* Parcourir les enfants de $array avec le chemin $keys fourni sous forme de
* tableau et retourner la valeur correspondante
*
* si $keys est vide ou null, retourner $default
*/
static final function _pget(?array $array, ?array $keys, $default=null) {
if (!$keys) return $default;
$value = $array;
$first = true;
foreach($keys as $key) {
if ($key === "" && $first) {
# une chaine vide en première position est ignorée
continue;
} elseif (is_array($value)) {
if (!array_key_exists($key, $value)) return $default;
$value = $value[$key];
} elseif ($value instanceof ArrayAccess) {
if (!$value->offsetExists($key)) return $default;
$value = $value->offsetGet($key);
} else {
return $default;
}
$first = false;
}
return $value;
}
/**
* Parcourir les enfants de $array avec le chemin $keys qui est de la forme
* key[.keys...] et retourner la valeur correspondante
*
* si $keys === null, retourner $default
*/
static final function pget_s(?array $array, ?string $keys, $default=null) {
if ($keys === null) return $default;
$keys = explode(".", $keys);
return self::_pget($array, $keys, $default);
}
/**
* Parcourir les enfants de $array avec le chemin $keys fourni sous forme de
* tableau et retourner la valeur correspondante
*
* si $keys est nul ou vide, retourner $default
*/
static final function pget_a(?array $array, ?array $keys, $default=null) {
if (!$keys) return $default;
$keys = implode(".", $keys);
$keys = explode(".", $keys);
return self::_pget($array, $keys, $default);
}
/** obtenir la valeur correspondant au chemin $keys dans $array */
static final function pget($array, $keys, $default=null) {
if ($keys === null) return $default;
elseif (is_array($keys)) return self::pget_a($array, $keys, $default);
else return self::pget_s($array, strval($keys), $default);
}
/**
* Modifier la valeur au chemin $keys fourni sous forme de tableau
*
* utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
* - _pset($array, [""], $value) est équivalent à $array[] = $value
* - _pset($array, ["a", "b", ""], $value) est équivalent à $array["a"]["b"][] = $value
* la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
*
* si $keys est vide ou null, $array est remplacé par $value
*/
static final function _pset(&$array, ?array $keys, $value): void {
if (!$keys) {
$array = $value;
return;
}
self::ensure_array($array);
$current =& $array;
$last = count($keys) - 1;
$i = 0;
foreach ($keys as $key) {
if ($i == $last) break;
if ($current instanceof ArrayAccess) {
if (!$current->offsetExists($key)) $current->offsetSet($key, []);
$current =& $current->offsetGet($key);
if ($current === null) {
$current = [];
} elseif (!is_array($current) && !($current instanceof ArrayAccess)) {
$current = [$current];
}
} else {
self::ensure_array($current[$key]);
$current =& $current[$key];
}
$i++;
}
if ($key === "") $current[] = $value;
else $current[$key] = $value;
}
/**
* Modifier la valeur au chemin $keys qui est de la forme key[.keys...]
*
* utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
* - pset($array, "", $value) est équivalent à $array[] = $value
* - pset($array, "a.b.", $value) est équivalent à $array["a"]["b"][] = $value
* la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
*
* si $keys est null, $array est remplacé par $value
*/
static final function pset_s(&$array, ?string $keys, $value): void {
if ($keys === null) {
$array = $value;
} else {
$keys = explode(".", $keys);
self::_pset($array, $keys, $value);
}
}
/**
* Modifier la valeur au chemin $keys fourni sous forme de tableau
*
* utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
* - pset($array, "", $value) est équivalent à $array[] = $value
* - pset($array, "a.b.", $value) est équivalent à $array["a"]["b"][] = $value
* la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
*
* si $keys est null, $array est remplacé par $value
*/
static final function pset_a(&$array, ?array $keys, $value): void {
if (!$keys) {
$array = $value;
} else {
$keys = implode(".", $keys);
$keys = explode(".", $keys);
self::_pset($array, $keys, $value);
}
}
/** modifier la valeur au chemin de clé $keys dans le tableau $array */
static final function pset(&$array, $keys, $value): void {
if ($keys === null) $array = $value;
elseif (is_array($keys)) self::pset_a($array, $keys, $value);
else self::pset_s($array, strval($keys), $value);
}
/**
* supprimer la valeur au chemin $keys fourni sous forme de tableau
*
* si $array vaut null ou false, sa valeur est inchangée.
* $keys est vide ou null, $array devient null
*/
static final function _pdel(&$array, ?array $keys): void {
if ($array === false || $array === null) return;
if (!$keys) {
$array = null;
return;
}
self::ensure_array($array);
$current =& $array;
$last = count($keys) - 1;
$i = 0;
foreach ($keys as $key) {
if ($i == $last) break;
if ($current instanceof ArrayAccess) {
if (!$current->offsetExists($key)) break;
} elseif (is_array($current)) {
if (!self::has($current, $key)) break;
} else {
break;
}
$current =& $current[$key];
$i++;
}
if ($i == $last) {
if ($current instanceof ArrayAccess) {
$current->offsetUnset($key);
} elseif (is_array($current)) {
unset($current[$key]);
}
}
}
/**
* supprimer la valeur au chemin $keys qui est de la forme key[.keys...]
*
* si $array vaut null ou false, sa valeur est inchangée.
* si $keys est null, $array devient null
*/
static final function pdel_s(&$array, ?string $keys): void {
if ($array === false || $array === null) return;
if ($keys === null) {
$array = null;
} else {
$keys = explode(".", $keys);
self::_pdel($array, $keys);
}
}
/**
* supprimer la valeur au chemin $keys fourni sous forme de tableau
*
* si $array vaut null ou false, sa valeur est inchangée.
* si $keys est vide ou null, $array devient null
*/
static final function pdel_a(&$array, ?array $keys): void {
if ($array === false || $array === null) return;
if (!$keys) {
$array = null;
} else {
$keys = implode(".", $keys);
$keys = explode(".", $keys);
self::_pdel($array, $keys);
}
}
/** supprimer la valeur au chemin de clé $keys dans $array */
static final function pdel(&$array, $keys): void {
if ($array === false || $array === null) return;
if ($keys === null) $array = null;
elseif (is_array($keys)) self::pdel_a($array, $keys);
else self::pdel_s($array, strval($keys));
}
#############################################################################
## map
static final function map(?array $array, callable $func): ?array {
if ($array === null) return null;
return array_map($func, $array);
}
#############################################################################
## filter
static final function filter_n(?array $array): ?array { return SL::filter_n($array); }
static final function filter_z(?array $array): ?array { return SL::filter_z($array); }
static final function filter_f(?array $array): ?array { return SL::filter_f($array); }
static final function filter_f2(?array $array): ?array { return SL::filter_pf($array); }
#############################################################################
## Tests sur les valeurs
static final function any_v(?array $array, $value, bool $strict=true): bool {
if ($strict) return SL::any_same($array, $value);
else return SL::any_equals($array, $value);
}
static final function all_v(?array $array, $value, bool $strict=true): bool {
if ($strict) return SL::all_same($array, $value);
else return SL::all_equals($array, $value);
}
static final function any_nv(?array $array, $value, bool $strict=true): bool {
if ($strict) return SL::any_not_same($array, $value);
else return SL::any_not_equals($array, $value);
}
static final function all_nv(?array $array, $value, bool $strict=true): bool {
if ($strict) return SL::all_not_same($array, $value);
else return SL::all_not_equals($array, $value);
}
static final function any_z(?array $array): bool { return SL::any_z($array); }
static final function all_z(?array $array): bool { return SL::all_z($array); }
static final function any_nz(?array $array): bool { return SL::any_nz($array); }
static final function all_nz(?array $array): bool { return SL::all_nz($array); }
static final function any_n(?array $array): bool { return SL::any_n($array); }
static final function all_n(?array $array): bool { return SL::all_n($array); }
static final function any_nn(?array $array): bool { return SL::any_nn($array); }
static final function all_nn(?array $array): bool { return SL::all_nn($array); }
static final function any_f(?array $array): bool { return SL::any_f($array); }
static final function all_f(?array $array): bool { return SL::all_f($array); }
static final function any_t(?array $array): bool { return SL::any_t($array); }
static final function all_t(?array $array): bool { return SL::all_t($array); }
static final function any_f2(?array $array): bool { return SL::any_pf($array); }
static final function all_f2(?array $array): bool { return SL::all_pf($array); }
static final function any_t2(?array $array): bool { return SL::any_pt($array); }
static final function all_t2(?array $array): bool { return SL::all_pt($array); }
#############################################################################
## Fonctions avancées
/** dans le tableau $array, "renommer" les clés selon le tableau $key_map */
static function map_keys(?array &$array, ?array $key_map): void {
if ($array === null || $key_map === null) return;
foreach ($key_map as $from => $to) {
if (array_key_exists($from, $array)) {
$array[$to] = $array[$from];
unset($array[$from]);
}
}
}
private static $flattener;
/**
* Applatir le tableau $array
*
* Pour chaque élément avec une clé séquentielle:
* - si c'est un tableau, l'applatir puis rajouter ses éléments tels quels au
* résultat
* - sinon ajouter l'élément tel quel
*
* Pour chaque élément avec une clé associative:
* - si la valeur n'existe pas déjà, elle est rajoutée telle quelle
* - si la valeur source est null ou false, la valeur destination n'est pas
* modifiée
* - sinon, les deux valeurs sont transformées en tableau le cas échéant.
* si $flattenValue == true, alors le tableau source est applati au préalable.
* puis cette nouvelle valeur est fusionnée avec array_merge() dans la valeur
* précédente.
*/
static final function flatten(?array &$array, bool $flattenValue=true): void {
if (self::$flattener === null) self::$flattener = new Flattener();
self::$flattener->flatten($array, $flattenValue);
}
/** retourner le tableau $array applati */
static final function flattened(?array $array, bool $flattenValue=true): array {
self::flatten($array, $flattenValue);
return $array;
}
/**
* Extraire d'un tableau les clés séquentielles
*
* Retourner $seq $seq est un tableau avec uniquement les valeurs des clés
* séquentielles. S'il n'existe aucune clé séquentielle retourner $default.
*
* Par exemple: extract_seq(["a", "b" => "c"]) retourne ["a"]
*/
static final function extract_seq(?array $array, ?array $default=null): ?array {
$seq = null;
if ($array !== null) {
$index = 0;
foreach ($array as $key => $value) {
if ($key === $index) {
$seq[] = $value;
$index++;
}
}
}
if ($seq === null) $seq = $default;
return $seq;
}
/**
* Extraire d'un tableau les clés associatives
*
* Retourner une liste $assoc $assoc est un tableau avec uniquement les
* valeurs des clés associatives. S'il n'existe aucune clé associative,
* retourner $default.
*
* Par exemple: split_assoc(["a", "b" => "c"]) retourne ["b" => "c"]
*/
static final function extract_assoc(?array $array, ?array $default=null): ?array {
$assoc = null;
if ($array !== null) {
$index = 0;
foreach ($array as $key => $value) {
if ($key === $index) $index++;
else $assoc[$key] = $value;
}
}
if ($assoc === null) $assoc = $default;
return $assoc;
}
/**
* Extraire d'un tableau les clés séquentielles et les clés associatives
*
* Retourner une liste [$seq, $assoc] $seq est un tableau avec uniquement
* les valeurs des clés séquentielles et $assoc est un tableau avec uniquement
* les valeurs des clés associatives. S'il n'existe aucune clé séquentielle
* (resp. aucune clé associative), $seq (resp. $assoc) vaut null.
*
* Par exemple: split_assoc(["a", "b" => "c"]) retourne [["a"], ["b" => "c"]]
*/
static final function split_assoc(?array $array): array {
$seq = null;
$assoc = null;
if ($array !== null) {
$i = 0;
foreach ($array as $key => $value) {
if ($key === $i) {
$seq[] = $value;
$i++;
} else {
$assoc[$key] = $value;
}
}
}
return [$seq, $assoc];
}
/**
* Joindre en un seul tableau un tableau avec des clés séquentielles et un
* tableau avec des clés associatives.
*
* Si $seq_first==true, les clés séquentielles arrivent d'abord, ensuite les
* clés associatives. Sinon, ce sont les clés associatives qui arrivent d'abord
*/
static final function merge_assoc(?array &$array, ?array $seq, ?array $assoc, bool $seq_first=false): void {
if ($seq === null && $assoc === null) $array = [];
elseif ($seq === null) $array = $assoc;
elseif ($assoc === null) $array = $seq;
elseif ($seq_first) $array = array_merge($seq, $assoc);
else $array = array_merge($assoc, $seq);
}
/**
* Construire un sous-ensemble du tableau $array en sélectionnant les clés
* mentionnées dans $keys
* .. si $keys === null, retourner $array
* .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
* chacune des clés séquentielles est prise telle quelle. les clés associatives
* permettent de renommer les clés
*
* soit $array = ["a" => 1, "b" => 2, "c" => 3]
* alors select($array, ["a", "b" => "x"])
* retourne ["a" => 1, "x" => 2]
*/
static final function select(?array $array, ?array $keys, $default=null): array {
if ($array === null) $array = [];
if ($keys === null) return $array;
$index = 0;
$result = [];
foreach ($keys as $key => $tkey) {
if ($key === $index) {
# clé séquentielle
$value = self::get($array, $tkey, $default);
$index++;
} else {
# clé associative
$value = self::get($array, $key, $default);
}
$result[$tkey] = $value;
}
return $result;
}
/**
* construire un sous-ensemble du tableau $array en sélectionnant les clés
* mentionnées dans $keys.
* .. si $keys === null, retourner $array
* .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
* pour chacune des clés séquentielles, la valeur est une clé pour récupérer
* la valeur dans $array
* pour chacune des clés associatives, la valeur est celle fournie
*/
static final function select_replace(?array $array, ?array $keys, $default=null): array {
if ($array === null) $array = [];
if ($keys === null) return $array;
$index = 0;
$result = [];
foreach ($keys as $key => $value) {
if ($key === $index) {
# clé séquentielle
$result[$value] = self::get($array, $value, $default);
$index++;
} else {
# clé associative
$result[$key] = $value;
}
}
return $result;
}
/**
* comme {@link select_replace()} mais $keys est applati d'abord
*
* par exemple:
* ~~~php
* $src = ["a" => 1, "b" => 2, "c" => 3];
* $dest = A::select($src, ["a", "x" => 9, ["z" => 8, "c"], "d"]);
* # $dest === ["a" => 1, "x" => 9, "z" => 8, "c" => 3, "d" => null]
* ~~~
*/
static final function select_replace2(?array $array, ?array $keys, $default=null): array {
if ($array === null) $array = [];
if ($keys === null) return $array;
self::flatten($keys);
return self::select_replace($array, $keys, $default);
}
/**
* construire un sous-ensemble du tableau $array en sélectionnant les clés
* mentionnées dans $keys.
* .. si $keys === null, retourner $array
* .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
* pour chacune des clés séquentielles, la valeur est une clé pour récupérer
* la valeur dans $array
* pour chacune des clés associatives, la valeur fournie est prise par défaut
* si la valeur correspondante n'existe pas ou vaut false dans $array
*/
static final function select_default(?array $array, ?array $keys, $default=null): array {
if ($array === null) $array = [];
if ($keys === null) return $array;
$index = 0;
$result = [];
foreach ($keys as $key => $value) {
if ($key === $index) {
# clé séquentielle
$result[$value] = self::get($array, $value, $default);
$index++;
} else {
# clé associative
if (array_key_exists($key, $array)) {
$arrayValue = $array[$key];
if ($arrayValue !== false) $value = $arrayValue;
}
$result[$key] = $value;
}
}
return $result;
}
/**
* comme {@link select_default()} mais $keys est applati d'abord
*
* par exemple:
* ~~~php
* $src = ["a" => 1, "b" => 2, "c" => 3];
* $dest = A::select($src, ["a", "x" => 9, ["z" => 8, "c"], "d"]);
* # $dest === ["a" => 1, "x" => 9, "z" => 8, "c" => 3, "d" => null]
* ~~~
*/
static final function select_default2(?array $array, ?array $keys, $default=null): array {
if ($array === null) $array = [];
if ($keys === null) return $array;
self::flatten($keys);
return self::select_default($array, $keys, $default);
}
/**
* Construire un sous-ensemble du tableau $array en sélectionnant les clés
* mentionnées dans $includes et pas mentionnées dans $excludes
*
* soit $array = ["a" => 1, "b" => 2, "c" => 3, "d" => 4]
* alors xselect($array, ["a", "b" => "x"], ["d"])
* retourne ["a" => 1, "x" => 2]
*/
static final function xselect_keys(?array $array, ?array $includes, ?array $excludes, $default=null): array {
if ($array === null) $array = [];
if ($includes === null) {
if ($excludes === null) return $array;
else $includes = array_keys($array);
}
$index = 0;
$result = [];
foreach ($includes as $fromkey => $tokey) {
if ($fromkey === $index) {
# clé séquentielle
$index++;
$fromkey = $tokey;
}
if ($excludes !== null && in_array($fromkey, $excludes)) continue;
$value = self::get($array, $fromkey, $default);
$result[$tokey] = $value;
}
return $result;
}
/**
* Construire un sous-ensemble du tableau $array en sélectionnant les valeurs
* mentionnées dans $includes et pas mentionnées dans $excludes
*
* soit $array = ["a" => 1, "b" => 2, "c" => 3, "d" => 4]
* alors xselect($array, ["a", "b" => "x"], ["d"])
* retourne ["a" => 1, "x" => 2]
*/
static final function xselect(?array $array, ?array $includes, ?array $excludes, $default=null): array {
if ($array === null) $array = [];
if ($includes === null && $excludes === null) return $array;
$result = [];
foreach ($array as $key => $value) {
if ($excludes !== null && in_array($value, $excludes)) continue;
if ($includes !== null && !in_array($value, $includes)) continue;
$result[$key] = $value;
}
return $result;
}
}