Search code examples
phphtmlarrayssortinggrouping

Sort and group a 2d array by two columns to form a hierarchical 3d array then display data as HTML lists


After sorting the $jobs array by location and territory_id values and then grouping them into arrays, I get an array which looks like the below - note I omitted most of the results inside each array since there's hundreds, but there should be a total of 18 arrays by territory_abbr key:

{
"PHL":[{"id":"28038a67e70c7e235fa725bbf8b6e167d9e3efee1e43a304d73be8408c6f24a2","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Aston, PA","territory_id":1,"territory_abbr":"PHL"},{"id":"7f8bcb16e0f3ffe9c8ce9aff2b6033bfbe582241e818aaf9c21506d509e70592","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Aston, PA","territory_id":1,"territory_abbr":"PHL"},{"id":"7c6cc1c2abda3dae2edf0982f758073316491b27c240e5fa13df791e0fb5b744","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Aston, PA","territory_id":1,"territory_abbr":"PHL"}],
"NJ":[{"id":"31f1752e8c3f1086b4a1ed499e8786e1d15a8d1825cb75848056333d1cae8276","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Bridgewater, NJ","territory_id":2,"territory_abbr":"NJ"},{"id":"a74e36d026e3e062aac973e07968934f2a88472fa9fb1028dea439b557bd82a1","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Bridgewater, NJ","territory_id":2,"territory_abbr":"NJ"},{"id":"889839e541fd5709077bd0c902d0183de8d091f694c1498e67853fd220c08ae9","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Bridgewater, NJ","territory_id":2,"territory_abbr":"NJ"}],
"MD":[{"id":"197c35000fb9b9ca1fe71771bb618c5443f0e7e51b59b9cad22f08d77d73de54","posting_job_title":"Siding Subcontractors Needed- Up To $10,000 SIGN ON BONUS","job_relationship":"company","location":"Aberdeen, MD","territory_id":3,"territory_abbr":"MD"},{"id":"8121cb7efb0e04130fbdc3f69468c7434c1582f71d71408ef689b81519d86a5c","posting_job_title":"Siding Subcontractors Needed- Up To $10,000 SIGN ON BONUS","job_relationship":"company","location":"Aberdeen, MD","territory_id":3,"territory_abbr":"MD"},{"id":"556b7ec84be0e6a5130cde5a441dc58ed595de5acd4e5f4883a2b2e6cc5eb4f4","posting_job_title":"Siding Subcontractors Needed- Up To $10,000 SIGN ON BONUS","job_relationship":"company","location":"Aberdeen, MD","territory_id":3,"territory_abbr":"MD"}],
"CT":[{"id":"b88924cfa34c25557381a63f9552074b4053f3e83dbe4c72564eb1cae3801a98","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Bronx, NY","territory_id":4,"territory_abbr":"CT"},{"id":"8621ae18266b571175358113fd3ed7147f2ffa7d248c71c8e1a78b566ac24628","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Bronx, NY","territory_id":4,"territory_abbr":"CT"},{"id":"bc2f7034687d2273f14bb2fa41fef8472f5e12ce8e483210b628167bba09c9aa","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Bronx, NY","territory_id":4,"territory_abbr":"CT"}],
"LI":[{"id":"5debdc91afd45080a7cb87912b9411f186793e3ad9be8fbf8fe7f40f85ce9d17","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Bayside, NY","territory_id":5,"territory_abbr":"LI"},{"id":"3dc256cecd93030f3d4079b5a313277cc03f92fb2d991cf9b0b656a34f84425f","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Bayside, NY","territory_id":5,"territory_abbr":"LI"},{"id":"269e4edc7ed745210c71e8b324a5f2f559ba793f1ff67d5e61b00ee48b992d89","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Bayside, NY","territory_id":5,"territory_abbr":"LI"}],
"BOS":[{"id":"498520c5b7fff98575b15dd442bc919b62bea6bcd5c3784a114e8c665a426013","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ashland, MA","territory_id":6,"territory_abbr":"BOS"},{"id":"7e3bbd3af87cf3260b5c7a2aa557d867c7c1be0e87122a3c5af4d9b8fad7457d","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ashland, MA","territory_id":6,"territory_abbr":"BOS"},{"id":"eccdf7304b1cb55fd77ed1a2b06ff07b8cf9885ab099a1e1b54b9eebc39fcaff","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ashland, MA","territory_id":6,"territory_abbr":"BOS"}],
"ATL":[{"id":"52d6c40d7bc5b3d4045bc8c62bbc534b1029873769a26ad9eb077c53412f1ebd","posting_job_title":"Remodeling Consultant","job_relationship":"w2_employee","location":"Alpharetta, GA","territory_id":7,"territory_abbr":"ATL"},{"id":"ad50ef5eb9633b913111923c8084e1a3c1b3c306cb1cec38bd946b9a93e9251d","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Alpharetta, GA","territory_id":7,"territory_abbr":"ATL"},{"id":"b256191b207364a1368d04f5d2e920383b8d140ec293e73f6a89b82033119e0d","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Alpharetta, GA","territory_id":7,"territory_abbr":"ATL"}],
"CHI":[{"id":"65341e3c52057461929c46ffbf590d846f504aedef36da6c6a4db36935200168","posting_job_title":"Roofing Installation Crews","job_relationship":"company","location":"Chicago, IL","territory_id":8,"territory_abbr":"CHI"},{"id":"4729ac73557d27ae83ca3a79cbe415bbfa64a6bd0b0db9a013db67a4a71fe9e5","posting_job_title":"Siding & Trim Installation Crews","job_relationship":"company","location":"Chicago, IL","territory_id":8,"territory_abbr":"CHI"},{"id":"f4473a812ad1b40d9b8e893afafa510f779c3077ecb38f73b9890809f0094ceb","posting_job_title":"Window & Door Installation Crews","job_relationship":"company","location":"Chicago, IL","territory_id":8,"territory_abbr":"CHI"}],
"DET":[{"id":"6c5f461dba3c81c77bbeb629769e82a84d63ebd30746fa8ebe90dc517873117c","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ann Arbor, MI","territory_id":9,"territory_abbr":"DET"},{"id":"e7d8a95c99486a03b0686eb591d60eca3a552d00220cf49184ffa9f43a21f4aa","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Ann Arbor, MI","territory_id":9,"territory_abbr":"DET"},{"id":"cb27d16da20e0c6f42b0285903a29fdc7edd51ed981e4133db8f7cf3948a3a19","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ann Arbor, MI","territory_id":9,"territory_abbr":"DET"}],
"HOU":[{"id":"20ba14a427a0d740a98150fafa7d428dac321072bc0889efcee9f57032c68634","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Cypress, TX","territory_id":10,"territory_abbr":"HOU"},{"id":"2e41c93a2c2243cafa24bbab0e5291f5a411b88b9ffdc2273d821a91621d8262","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Cypress, TX","territory_id":10,"territory_abbr":"HOU"},{"id":"3e2957a877ac2d7d3fcb02e52ba87303b78e8393beef17ffb1fe56f2a544d7a3","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Cypress, TX","territory_id":10,"territory_abbr":"HOU"}],
"DAL":[{"id":"8a4f2e8d049566039672b2b411336a8b43de3035f6fc8fa92ff8c2b190ddc96b","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Arlington, TX","territory_id":11,"territory_abbr":"DAL"},{"id":"6de607ce972c23824d32179728282a0d2bc2c9bba4b3c6d9113b70975712e9d2","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Arlington, TX","territory_id":11,"territory_abbr":"DAL"},{"id":"80b8fcb5cc6d581c4fc1e9be0bd48dd8db1b73474cdc920ed3a9665211cbbae4","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Arlington, TX","territory_id":11,"territory_abbr":"DAL"}],
"DEN":[{"id":"a8f74d5ec1d31e3e43c4b6546631da15a5feb58f008adb4d6717e4627e25e68e","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Aurora, CO","territory_id":12,"territory_abbr":"DEN"},{"id":"6b36d913d8e36d5e4c6a14558a50627bb5bcae278ac794c010864304be6b7400","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Aurora, CO","territory_id":12,"territory_abbr":"DEN"},{"id":"18f03346479a88c66f8b9e91ad4df042beff28270ddbd45cffd089be24a01fa0","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Aurora, CO","territory_id":12,"territory_abbr":"DEN"}],
"TPA":[{"id":"06e9a1a02878dc977e59234a79fad805f732c4f92a9a3db717baa8ec1cb0f69c","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Lakeland, FL","territory_id":13,"territory_abbr":"TPA"},{"id":"0b10403a41d496052c30df684964e06165ea491b6a5ff29aa4ea818e1c9c7110","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Lakeland, FL","territory_id":13,"territory_abbr":"TPA"},{"id":"d02541fc1d4df726375dce49140845b38c4c79e08c6b7c41e328adb160d8da51","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Lakeland, FL","territory_id":13,"territory_abbr":"TPA"}],
"AUS":[{"id":"452a16def02ae6d55fa9ea6cc2135a5a755954305c77b44587a7810622177ee4","posting_job_title":"Customer Development Representative","job_relationship":"w2_employee","location":"Austin, TX","territory_id":14,"territory_abbr":"AUS"},{"id":"3c8d9a8153faacbdde42799cd4a8f4f985522c1aa38a22d7838333c21376f78c","posting_job_title":"Window & Door Installation Crews","job_relationship":"company","location":"Austin, TX","territory_id":14,"territory_abbr":"AUS"},{"id":"65af00695e086cbebeb142f46a2f1e14f4f6665e77975b388cf4371bd083a485","posting_job_title":"Roofing Installation Crews","job_relationship":"company","location":"Austin, TX","territory_id":14,"territory_abbr":"AUS"}],
"CLT":[{"id":"00c184d45d6d237e6315f8fdf35f35498fd401fd6e5a24623600bf4ff3041c87","posting_job_title":"Customer Development Representative","job_relationship":"w2_employee","location":"Charlotte, NC","territory_id":15,"territory_abbr":"CLT"},{"id":"97a21226497c970195adda721302242629be3d6b4eb9245d5907b93fbd249f18","posting_job_title":"Window & Door Installation Crews","job_relationship":"company","location":"Charlotte, NC","territory_id":15,"territory_abbr":"CLT"},{"id":"06c15280da60ddfc62014894d5bd68a3dc374e0e3a6f360b76d3a5f022c580b0","posting_job_title":"Roofing Installation Crews","job_relationship":"company","location":"Charlotte, NC","territory_id":15,"territory_abbr":"CLT"}],
"NSH":[{"id":"624b363379c3963f804e170b02e39402322c27cd8da6ebac9861866530586d3a","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Ashland City, TN","territory_id":16,"territory_abbr":"NSH"},{"id":"c44f268f5059e0b88652cb36e651ead19f0b7998deeec980dd52c7ca1ccdd1af","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ashland City, TN","territory_id":16,"territory_abbr":"NSH"},{"id":"4c7f01b5e12adcfa38518d7687cd1201ec01c9b5b4cd621e3341fddedfab33cf","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Ashland City, TN","territory_id":16,"territory_abbr":"NSH"}],
"PHX":[{"id":"30afd9bf92825b4f69b0d57bdafd72f0cc054b135506a1236ce5eb54702694ad","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Glendale, AZ","territory_id":17,"territory_abbr":"PHX"},{"id":"5cf2990ca82ca1b2d43c40aa253458ce9cd05b0311a18ac39f7df274f99fb30d","posting_job_title":"Solar Sales Representative","job_relationship":"w2_employee","location":"Glendale, AZ","territory_id":17,"territory_abbr":"PHX"},{"id":"f9ca1b7db38430485feb0d285048527dd4fbf01c79fc43b47a1d6d9b270d9128","posting_job_title":"Sales Representative*","job_relationship":"w2_employee","location":"Glendale, AZ","territory_id":17,"territory_abbr":"PHX"}],
"PIT":[{"id":"be274ea74b2be7f448ddc28a1325957dad55b2a83dd0bc4141f8662965e641bc","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Chester, WV","territory_id":18,"territory_abbr":"PIT"},{"id":"9027f7f306b7d852ba544af54e0eb7956fdef5d17fd4d2cc619b8dba0ea45c79","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Chester, WV","territory_id":18,"territory_abbr":"PIT"},{"id":"331fd3928cc62352710e99070c3f91bc61ee72126deb121e55803e18a3bd9cc8","posting_job_title":"Entry Level Sales Representative*","job_relationship":"w2_employee","location":"Chester, WV","territory_id":18,"territory_abbr":"PIT"}]
}

The end goal is to take the above arrays and display each arrays' content so that:

  • each orange box would be a DIV
  • these will be coded as accordion toggles, where each territory_abbr can be opened to reveal each location and jobs available within each location
  • a total of 18 toggles (one for each territory_abbr)

enter image description here

Below is the code I have so far. I'm able to output majority of this correctly, but I can't figure out how to get the location value before each $value array in the code.

function display_jobs(){
    $jobs = get_jobs();

    //sort by territory_id and location
    usort($jobs, function($a, $b) { 
        return $a['territory_id'] <=> $b['territory_id'] ?: $a['location'] <=> $b['location'];
    });

    //group by location, display results.
    $group = array();
    foreach ($jobs as $posting) {
        $group[$posting['territory_abbr']][] = $posting;
    }

    $results .= "<div>"; // accordion
    $results .= "<div>"; // toggle
    
    //for each location
    foreach($group as $key=>$value){
            $results .= "<h3>" . $key . "</h3>"; // toggle title
        $results .= "<div>"; // toggle content
        $results .= "<h4>location name</h4>";
        $results .= "<div>";

        foreach ($value as $k=>$v) {
            //list posting_job_title for each location
            $results .= "<div>";
                $results .= "<ul class='col'>";
                    $results .= "<li>" . $v['territory_abbr'] . "</li>";
                    $results .= "<li>" . $v['territory_id'] . "</li>";
                    $results .= "<li>" . $v['location'] . "</li>";
                    $results .= "<li>" . $v['id'] . "</li>";
                    $results .= "<li>" . $v['posting_job_title'] . "</li>";
                    $results .= "<li>" . $v['job_relationship'] . "</li>";
                $results .= "</ul>";
            $results .= "</div>";
        }
        $results .= "</div>";
        $results .= "</div>";
    }


    $results .= "</div>";
    $results .= "</div>";
    return $results;
}

Solution

  • Thanks to @Barmar, added another level of nesting of associative arrays, making location the key of these arrays.

    Below is my updated code.

    function display_jobs(){
        $jobs = get_jobs();
    
        //sort by territory_id and location
        usort($jobs, function($a, $b) { 
            return $a['territory_id'] <=> $b['territory_id'] ?: $a['location'] <=> $b['location'];
        });
    
        //group by location, display results.
        $group = array();
        foreach ($jobs as $posting) {
            $group[$posting['territory_abbr']][$posting['location']][] = $posting;
        }
    
        $results .= "<div>"; // accordion
        
        //for each territory_abbr
        foreach($group as $key=>$value){
            $results .= "<div>"; // toggle
            $results .= "<h3>" . $key . "</h3>"; // territory_abbr
            foreach($value as $k=>$v){
                $results .= "<div>"; // toggle content
                $results .= "<div>"; // extra wrapper
                $results .= "<h4 class='h5'>".$k."</h4>"; // location name
                foreach ($v as $k2=>$v2) {
                    //list job info for each location
                    $results .= "<div>";
                        $results .= "<ul class='col'>";
                                $results .= "<li>" . $v2['territory_abbr'] . "</li>";
                                $results .= "<li>" . $v2['territory_id'] . "</li>";
                                $results .= "<li>" . $v2['location'] . "</li>";
                                $results .= "<li>" . $v2['id'] . "</li>";
                                $results .= "<li>" . $v2['posting_job_title'] . "</li>";
                                $results .= "<li>" . $v2['job_relationship'] . "</li>";
                        $results .= "</ul>";
                    $results .= "</div>";
                }
                $results .= "</div>";
                $results .= "</div>";
            }
    
            $results .= "</div>";
    
        }
        $results .= "</div>";
        return $results;
    }
    111