web-dev-qa-db-fra.com

Manière élégante de rechercher un PHP tableau utilisant une fonction définie par l'utilisateur

En gros, je veux pouvoir obtenir les fonctionnalités de find_if() de C++, de detect: de Smalltalk, etc.:

// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });

Mais je ne connais aucune fonction PHP qui effectue cela. Une "approximation" je suis venu avec:

$check = array_filter($myArray, function($element) { ... });
if ($check) 
    //...

L'inconvénient est que le but du code n'est pas clair. De plus, cela n'arrêtera pas d'itérer sur le tableau même si l'élément a été trouvé, bien que ce soit plus qu'un nitpick (si le jeu de données est assez grand pour causer des problèmes, la recherche linéaire ne sera de toute façon pas une réponse)

23
lethal-guitar

Pour extraire le premier du tableau ou renvoyer false:

current(array_filter($myArray, function($element) { ... }))

Plus d'infos sur current () ici .

39
Izkata

Voici une solution de base

function array_find($xs, $f) {
  foreach ($xs as $x) {
    if (call_user_func($f, $x) === true)
      return $x;
  }
  return null;
}

array_find([1,2,3,4,5,6], function($x) { return $x > 4; });  // 5
array_find([1,2,3,4,5,6], function($x) { return $x > 10; }); // null

Si l'événement $f($x) renvoie true, la boucle est court-circuitée et $x est immédiatement renvoyé. Comparé à array_filter, cela est préférable pour notre cas d'utilisation car array_find ne doit pas continuer à itérer après la première correspondance positive.

Si le rappel ne renvoie jamais la valeur true, la valeur null est renvoyée.


Remarque, j'ai utilisé call_user_func($f, $x) au lieu d'appeler simplement $f($x). Ceci est approprié ici car il vous permet d'utiliser n'importe quel compatible callable

Class Foo {
  static private $data = 'z';
  static public function match($x) {
    return $x === self::$data;
  }
}

array_find(['x', 'y', 'z', 1, 2, 3], ['Foo', 'match']); // 'z'

Bien sûr, cela fonctionne aussi pour les structures de données plus complexes

$data = [
  (object) ['id' => 1, 'value' => 'x'],
  (object) ['id' => 2, 'value' => 'y'],
  (object) ['id' => 3, 'value' => 'z']
];

array_find($data, function($x) { return $x->id === 3; });
// stdClass Object (
//     [id] => 3
//     [value] => z
// )

Si vous utilisez PHP 7, ajoutez des conseils de type.

function array_find(array $xs, callable $f) { ...
20
user633183

Le array_search d'origine renvoie la clé de la valeur correspondante, et non la valeur elle-même (cela peut être utile si vous souhaitez modifier le tableau d'origine ultérieurement).

essayez cette fonction (cela fonctionne aussi avec les tableaux associatifs)

function array_search_func(array $arr, $func)
{
    foreach ($arr as $key => $v)
        if ($func($v))
            return $key;

    return false;
}
3
Roey

Vous pouvez écrire une telle fonction vous-même, même s’il ne s’agit guère que d’une boucle.

Par exemple, cette fonction vous permet de passer une fonction de rappel. Le rappel peut renvoyer 0 ou une valeur. Le rappel que je spécifie renvoie l'entier s'il est> 10. La fonction s'arrête lorsque le rappel renvoie une valeur non NULL.

function check_in_array(array $array, $callback)
{
  foreach($array as $item)
  {
    $value = call_user_func($callback, $item);
    if ($value !== null)
      return $value;
  }
}

$a = array(1, 2, 3, 6, 9, 11, 15);
echo check_in_array($a, function($i){ return ($i > 10?$i:null); });
0
GolezTrol

Utilisez \iter\search() de la bibliothèque d'iters des fonctions d'itération primitives de nikic. Il présente l'avantage supplémentaire de fonctionner sur les collections de tableaux etTraversable.

$foundItem = \iter\search(function ($item) {
    return $item > 10;
}, range(1, 20));

if ($foundItem !== null) {
    echo $foundItem; // 11
}
0