1

I'm building out a careers page that reads the available positions from a hosted json file. I'm having difficulty getting the data to filter correctly by more than on value.

This example tries to output only the matching values from the $data array when comparing with key/values set in the $filter array:

<?php
    /* ----------------------
    Dump the output
    ---------------------- */
    function dump($data) {
        if(is_array($data)) { //If the given variable is an array, print using the print_r function.
            print "<pre>\n";
            print_r($data);
            print "</pre>";
        } elseif (is_object($data)) {
            print "<pre>\n";
            var_dump($data);
            print "</pre>";
        } else {
            print "=========&gt; ";
            var_dump($data);
            print " &lt;=========";
        }
    }

    /* ----------------------
    Sanitize a string to url friendly
    ---------------------- */
    function cleanString($str) {
        $removetags = array("'", '"', ',', '.', '?', '& ', '&amp; ', '/', '#', '@','(',')');
        $removespace[] = ' ';
        $notags = str_replace($removetags,"",$str);
        $nospaces = strtolower(str_replace($removespace,"-",$notags));
        return preg_replace('~-{2,}~', '-', $nospaces);
    }
    function sanitizeString($val) {
        if(is_array($val)) {
            $array = array() ;
            foreach ($val as $key => $value) {
                $array[$key] = cleanString($value);
            }
            return $array;
        } else {
            $result = cleanString($val);
        }
        return $result;
    }

    /* ----------------------
    Filter the array data, should match all values;=
    ---------------------- */
    function filterData($array, $filter) {
        $result = array();
        foreach ($filter as $gk => $gv) {
            foreach ($array as $key => $value) {
                if (array_key_exists($gk, $value) && !empty($value[$gk])) {
                    if(is_array($value[$gk])) {
                        $child = array_search($gv, sanitizeString($value[$gk]));
                        if(!empty($value[$gk][$child])) {
                            $theValue = sanitizeString($value[$gk][$child]);
                            if ($theValue ===  $gv ) {
                                array_push($result,$value);
                            }
                        }
                    } else {
                        $theValue = sanitizeString($value[$gk]);
                        if ($theValue ===  $gv ) {
                            array_push($result,$value);
                        }
                    }
                }
            }
        }
        return (!empty($result)) ? $result : $array;
    };

    // sample data
    $data = '{"jobs":[{"department":"sales","location":{"country":"United States","country_code":"US","region":"New York","region_code":"NY","city":"New York","zip_code":null,"telecommuting":false}},{"department":null,"location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"Leeds","zip_code":null,"telecommuting":false}},{"department":"Project Management","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"Manchester","zip_code":null,"telecommuting":false}},{"department":"Project Management","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}},{"department":"Customer Success","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}},{"department":null,"location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}}]}';

    $decode = json_decode($data, true);


    // sample filter - values should be lowercase, and sanitised;
    $filter = array (
        'department' => 'project-management',
        'location' => 'london'
    );

    $filterdata = filterData($decode['jobs'], $filter);

    $i = 1;
    foreach ($filterdata as $res) {
        echo $i . '<br />';
        echo $res['department'] . '<br />';
        echo $res['location']['city'] . '<hr />';
        $i++;
    }
?>

What I'm finding is that the filterData function will return results that match any set key/value in the $filter array. So I can get duplicate entries if a department and location are both matched.

How do I go about adjusting this so that I only get the array output that where all $filter key/values match?

Thanks - I've spent a day on this and am no futher, so any help in the right direction would be greatly appreciated.

5
  • It's not entirely clear what your problem is (despite a very well-worded question). Do you have TWO problems (one, that you get duplicates, two, that your filter isn't specific enough)? If so, you need two fixes ... Commented May 8, 2018 at 16:17
  • Incidentally, if it's just a matter of duplicates, there's array_unique(). You may have already tried that. Commented May 8, 2018 at 16:18
  • @Kevin_Kinsey - The problem is I can end up with results that match a department as well as any results that match the set location, rather than exactly matching both. If you check this example eval.in/1001125 - it will show all entries for project-managment and all entries for london, which due the to error shows entries based in manchester Commented May 8, 2018 at 16:25
  • Are you only going to be matching on department and location? Or does this need to be able to use any number of criteria (that is, array $filter may have 2, 3, 4, or $n members)? Commented May 8, 2018 at 16:45
  • Yes. This is supposed to scale for any number of criteria. Commented May 8, 2018 at 16:49

1 Answer 1

2

Note: I may didn't understand your question quite well but, seeing there is no answer yet, I decided to try. If my guess is wrong, let me know and I will modify my answer accordingly.

I am gonna ignore your cleanString & sanitizeString function for now. I believe you can implement those easily after solving the job-filtering part.

To filter any array of data, array_filter() is your best friend.

array_filter() Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array. Array keys are preserved.

Inside array_filter() callback, loop over your filter rules, and check if the $job has matched property for it.
If All rules matched, we will return true, otherwise false (which will filter out that job out of the array)

$data = '{"jobs":[{"department":"sales","location":{"country":"United States","country_code":"US","region":"New York","region_code":"NY","city":"New York","zip_code":null,"telecommuting":false}},{"department":null,"location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"Leeds","zip_code":null,"telecommuting":false}},{"department":"Project Management","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"Manchester","zip_code":null,"telecommuting":false}},{"department":"Project Management","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}},{"department":"Customer Success","location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}},{"department":null,"location":{"country":"United Kingdom","country_code":"GB","region":"England","region_code":"England","city":"London","zip_code":null,"telecommuting":false}}]}';

    $decode = json_decode($data, true);

    $filters = array(
        'department' => 'Project Management',
        'location' => 'London'
    );

    /**
     * We will use array_filter()
     * to filter out jobs that doesn't meet our rules
     */
    $filterData = array_filter($decode['jobs'], function($job) use($filters) {
        $matched = [];

        foreach ($filters as $col => $val) {
            if (isset($job[$col]) === false) {
                $matched[0] = '';
                continue;
            }

            /**
             * I am assigning filter condition to $matched key
             * PHP as a dynamic language, will convert boolean to integer
             * 
             * If you don't like this approach, you can try assigning to value
             */
            if (is_array($job[$col])) {
                $matched[in_array($val, $job[$col])] = '';
            } else {
                $matched[($job[$col] === $val)] = '';
            }
        }

        /**
         * If any of our rules returned false
         * We will return false too
         * and array_filter will filter out this job
         */
        return isset($matched[0]) === false;
    });

    print_r($filterData);
Sign up to request clarification or add additional context in comments.

1 Comment

Wow, thanks. Someone else also suggested array_filter which i'd not used before, so was abouts to have a dig through. Thanks again, that seems to return the expected output :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.