1
\$\begingroup\$

I need to write a function that searches the two-tier board according to certain parameters, which works, but I question whether there is a simpler, lighter way to perform this task.

<?php
function findValue(array $array, array $parameters, $multipleResoult = false){
    $result = array();//used when $multipleResoult == true
    $suspicious = false;
    foreach($array as $childArray){
        foreach($parameters as $k => $p){
            if(array_key_exists($k,$childArray)){
                if($childArray[$k] == $p){
                    $suspicious = $childArray;
                } else {
                    $suspicious = false;
                    continue 2;
                }
            } else {
                $suspicious = false;
                continue 2;
            }
        }
        if(is_array($suspicious)){
            $result[] = $suspicious;
            if($multipleResoult == true){
                $suspicious = false;
            } else {            
                break;
            }
        }

    }
    return $result;
}
$arr =     Array
    ( 
        0 => Array
        (
            "id" => 1,
            "id_shop" => 1,
            "id_lang" => 1,
            "id_product" => 1,
            "id_field" => 3,
            "field_value" => "zxczxc"
        ),

    // find if single and multiple
    1 => Array
    (
            "id" => 2,
            "id_shop" => 1,
            "id_lang" => 2,
            "id_product" => 1,
            "id_field" => 3,
            "field_value" => "sdfsdfsdf"
        ),

    2 => Array
    (
            "id" => 3,
            "id_shop" => 1,
            "id_lang" => 2,
            "id_product" => 2,
            "id_field" => 3,
            "field_value" => "sdfsdfsdf"
        ),
    // find if multiple
    3 => Array
    (
            "id" => 3,
            "id_shop" => 1,
            "id_lang" => 2,
            "id_product" => 1,
            "id_field" => 3,
            "field_value" => "sdfsdfsdf"
        )

);
echo '-----------------SINGLE----------------------';echo "\n";echo '<pre>';
print_r(findValue($arr, array(
    'id_shop' => 1,
    'id_field' => 3,
    'id_lang'=> 2,
    'id_product' => 1)
                 ));
echo '-----------------MULTIPLE----------------------';echo "\n";echo '<pre>';
print_r(findValue($arr, array(
    'id_shop' => 1,
    'id_field' => 3,
    'id_lang'=> 2,
    'id_product' => 1)
                 ,true));
?>
\$\endgroup\$
1
\$\begingroup\$

Firstly, this could use a better description (in a comment) explaining what it does. I had to figure it out from the code. I think the description should probably be something like

// Searches $array for associative arrays which are supersets of $parameters.
// If $multipleResults it will return all matches in an array;
// otherwise just the first, in an array.

Secondly, names.

  • findValue isn't very descriptive. How about findSupersets?
  • $array would traditionally be called $haystack in PHP, since you're searching through it for matches to a predicate.
  • $multipleResoult is a misspelling. Correcting the spelling and adjective-noun agreement we get $multipleResults, which is ok, although I personally would prefer to communicate an action: $returnAll.
  • $suspicious has the wrong connotations for this native English speaker. If something is suspicious, it means that I think there's something wrong with it. My preferred variable name for something which may or may not pass an acceptance test is $candidate.

Thirdly, since PHP has a dynamic type system I think it would be more idiomatic to return just the element if !$multipleResults, rather than wrapping it in an array.


Now, you specifically ask whether there's a simpler way to do it. I think that using booleans it can be simplified quite a bit by inverting the assumption. Rather than prove it acceptable, prove it not acceptable. Taking into account various of my suggestions above and refactoring I get:

    foreach($haystack as $childArray){
        $isCandidate = true;
        foreach($parameters as $k => $p){
            if(!array_key_exists($k,$childArray) || $childArray[$k] != $p){
                $isCandidate = false;
                break;
            }
        }
        if($isCandidate){
            if(!$multipleResults){
                return $childArray;
            }
            $result[] = $childArray;
        }
    }

    return $result;

It's a couple of years now since I used PHP, and I'm not sure offhand whether there are built-in functions to test whether $childArray contains $parameters as a subset, or to filter an array by a predicate. It may be that the whole function can be reduced to two calls to built-ins. If not, you could consider whether you want to factor it out as

function findMatch(array $haystack, $predicate, $returnAll = false) {
    $results = []
    foreach ($haystack as $element) {
        if ($predicate($element)) {
            if ($returnAll) return $element;
            $results[] = $element;
        }
    }
    return $results;
}

function isSuperset(array $candidate, array $subset) {
    foreach ($subset as $k => $v) {
        if (!isset($candidate[$k]) || $candidate[$k] != $v) return false;
    }
    return true;
}

linking the two with a lambda.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

You don't need to call a second foreach loop and temporarily store partial matches (suspicious/candidates) as you go.
Use array_intersect_assoc() and sizeof() (or count()) to instantly identify qualifying subarrays.

Code: (Demo)

function findValue(array $array, array $terms, $get_all=false){
    $term_count=sizeof($terms);  // cache the element count of $terms
    $results=[]; // establish empty array for print_r if no matches are found.
    foreach($array as $subarray){
        if(sizeof(array_intersect_assoc($subarray,$terms))==$term_count){ // qualifying subarray
            if(!$get_all){
                return $subarray;  // end loop and return the single subarray
            }else{
                $results[]=$subarray;
            }
        }
    }
    return $results;
}
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.