phparrayssortingmultidimensional-array

(PHP) specific array sort


I am stuck to an array structure and need to sort them. Here is a small example of the array:

$brand["BRAND_1"]["name"] = 'Brand Name 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['name'] = 'Headline Subbrand 2';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['text'] = 'text for Subbrand 2';

$brand["BRAND_2"]["name"] = 'Brand Name 2';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';

There could be n amount of BRAND_N and n amount of SUB_BRAND_N.

I have an order ID which stucks to a flexible order:

0 = out as in (dont worry about this, catch it at an earlier stage)
1 = BRAND_1.SUB_BRAND_1
2 = BRAND_1.SUB_BRAND_2
3 = BRAND_2.SUB_BRAND_1

This gets extended if there are more as these four cases and the order itself could change. So I have to work with the IDs.

The order should change the position in the array and push the value of the Id to the top in the array, for example:

order id = 2:

$brand["BRAND_1"]["name"] = 'Brand Name 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['name'] = 'Headline Subbrand 2';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['text'] = 'text for Subbrand 2';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';

$brand["BRAND_2"]["name"] = 'Brand Name 2';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';

order id = 3:

$brand["BRAND_2"]["name"] = 'Brand Name 2';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_2"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';

$brand["BRAND_1"]["name"] = 'Brand Name 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['name'] = 'Headline Subbrand 2';
$brand["BRAND_1"]["list"]['SUB_BRAND_2']['text'] = 'text for Subbrand 2';

I have tried this so far:

<?php

$order[1] = 'BRAND_1::SUB_BRAND_1';
$order[2] = 'BRAND_1::SUB_BRAND_2';
$order[3] = 'BRAND_2::SUB_BRAND_1';

// for testing
$orderId = 2;

// get the brand and sub_brand
$part = explode("::", $order[$orderId]); 

// set the new brand list
$newBrand[$part[0]]['name'] = $brand[$part[0]]['name'];
$newBrand[$part[0]]['list'][$part[1]] = $brand[$part[0]]['list'][$part[1]];

// unset the brand which should be first of the main brand array
unset($brand[$part[0]]['list'][$part[1]]);

// if there was only one list unset the whole brand part
if( count($brand[$part[0]]['list']) < 1 ) {
    unset( $brand[$part[0]] );
}

Now I have two arrays:

$brand includes the whole brands except this one which should be the first

$newBrand includes only this brand which should be at the top

Now I just need to add $brand to $newBrand but there is my problem.

Tried a lot of different ways from rebuilding the array to push the content, replace or merge... but I always run in circles.

I wrote the whole code into codepad.org for better testing: Codepad example

EDIT:

The order is a specification outside the code. The aim is to set one element depending on the orderId to the top in the $brand array. The $orderId will get passed by POST, GET or a class call.

The $order array is just an array which helps me to make the specification accessible in the code.

So the $orderId match one element of the $order array and will return this element which should be on the top of the $brand array. Due to the fact that there is no numeric keys I decide to use the "brand::sub_brand" syntax to access on both depth level.


Solution

  • Here is one possible solution (test it here):

    <?php
    
    function getOrderingRules()
    {
        return array(
            1 => 'BRAND_1.SUB_BRAND_1',
            2 => 'BRAND_1.SUB_BRAND_2',
            3 => 'BRAND_2.SUB_BRAND_1',
        );
    }
    
    function getOrderedBrands($brands, $orderId)
    {
        $rules = getOrderingRules();
        if (!isset($rules[$orderId])) {
            throw new RuntimeException("Rule for order id '$orderId' is not specified");
        }
    
        $result = array();
    
        // Push the first element
        list($key, $subkey) = explode('.', $rules[$orderId]);
        $result[$key] = array(
            'name' => $brands[$key]['name'],
            'list' => array(
                $subkey => $brands[$key]['list'][$subkey],
            ),
        );
    
        // Push remaining elements in the order they appear in $rules
        foreach ($rules as $oid => $rule) {
            // Skip order id of the first element
            if ($oid == $orderId) {
                continue;
            }
            list($key, $subkey) = explode('.', $rules[$oid]);
            if (!isset($result[$key])) {
                $result[$key] = array(
                    'name' => $brands[$key]['name'],
                    'list' => array(),
                );
            }
            $result[$key]['list'][$subkey] = $brands[$key]['list'][$subkey];
        }
    
        return $result;
    }
    
    // Loading all brands (could be external source, like database)
    $brand["BRAND_1"]["name"] = 'Brand Name 1';
    $brand["BRAND_1"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
    $brand["BRAND_1"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';
    $brand["BRAND_1"]["list"]['SUB_BRAND_2']['name'] = 'Headline Subbrand 2';
    $brand["BRAND_1"]["list"]['SUB_BRAND_2']['text'] = 'text for Subbrand 2';
    
    $brand["BRAND_2"]["name"] = 'Brand Name 2';
    $brand["BRAND_2"]["list"]['SUB_BRAND_1']['name'] = 'Headline Subbrand 1';
    $brand["BRAND_2"]["list"]['SUB_BRAND_1']['text'] = 'text for Subbrand 1';
    
    // Sort and output
    print_r(getOrderedBrands($brand, 1));
    print_r(getOrderedBrands($brand, 2));
    print_r(getOrderedBrands($brand, 3));
    

    You should be aware that with this array structure ($brands) you won't be able to set ordering rules like this:

    1 = BRAND_1.SUB_BRAND_1
    2 = BRAND_2.SUB_BRAND_1
    3 = BRAND_1.SUB_BRAND_2
    

    because once you met element keyed by BRAND_1, you have to iterate over all of it's sub-brands. If you don't have such rules, everything is fine. Otherwise you have to store sorted array structured like this (because actually you are sorting sub-brands, not brands):

    $subBrands = array(
        array(
            'name' => 'Headline Subbrand 1',
            'text' => 'Text for it',
            'parent' => 'BRAND_1',
            'key' => 'SUB_BRAND_1',
        ),
        array(
            'name' => 'Headline Subbrand 2',
            'text' => 'Text for it',
            'parent' => 'BRAND_1',
            'key' => 'SUB_BRAND_2',
        ),
    );
    $parentBrands = array(
        'BRAND_1' => 'Brand Name 1',
        'BRAND_2' => 'Brand Name 2',
    );
    

    Then you can sort $subBrands and iterate over it