Search code examples
phparray-filter

array_filter with element modification


I'm trying to make a clean array from a string that my users will define. The string can contain non-valid IDs, spaces, etc. I'm checking the elements using a value object in a callback function for array_filter.

$definedIds = "123,1234,1243, 12434 , asdf"; //from users panel

$validIds = array_map(
    'trim',
    array_filter(
        explode(",", $definedIds),
        function ($i) {
            try {
                new Id(trim($i));
                return true;
            } catch (\Exception $e) {
                return false;
            }
        }
    )
);

This works fine, but I'm applying trim twice. Is there a better way to do this or a different PHP function in which I can modify the element before keeping it in the returned array?

NOTE: I also could call array_map in the first parameter of array_filter, but I would be looping through the array twice anyway.


Solution

  • It depends on whether you care about performance. If you do, don't use map+filter, but use a plain for loop and manipulate your array in place:

    $arr = explode(',', $input);
    
    for($i=count($arr)-1; $i>=0; $i--) {
      // make this return trimmed string, or false,
      // and have it trim the input instead of doing
      // that upfront before passing it into the function.
      $v = $arr[$i] = Id.makeValid($arr[$i]);
    
      // weed out invalid ids
      if ($v === false) {
        array_splice($arr, $i, 1);
      }
    }
    
    // at this point, $arr only contains valid, cleaned ids
    

    Of course, if this is inconsequential code, then trimming twice is really not going to make a performance difference, but you can still clean things up:

    $arr = explode(',', $input);
    $arr = array_filter(
      array_map('Id.isValidId', $arr),
      function ($i) {
        return $i !== false;
      }
    );  
    

    In this example we first map using that function, so we get an array of ids and false values, and then we filter that so that everything that's false gets thrown away, rather than first filtering, and then mapping.

    (In both cases the code that's responsible for checking validity is in the Id class, and it either returns a cleaned id, or false)