3

I'm trying to build up a result array from a DB query and basicly I was wondering if the following would be possible

Array content:

Array
(
    [0] => Array
        (
            [Section_Id] => 1
            [Section_Name] => "Section1"
            [Section_Parent] => NULL
            [Section_Position] => 0
            [Section_Depth] => 0
        )

    [1] => Array
        (
            [Section_Id] => 3
            [Section_Name] => "Section2"
            [Section_Parent] => NULL
            [Section_Position] => 1
            [Section_Depth] => 0

        )

    [2] => Array
        (
            [Section_Id] => 4
            [Section_Name] => "SubSection1ForSection2"
            [Section_Parent] => 3
            [Section_Position] => 0
            [Section_Depth] => 1
        )

    [3] => Array
        (
            [Section_Id] => 2
            [Section_Name] => "SubSection1ForSection1"
            [Section_Parent] => 1
            [Section_Position] => 0
            [Section_Depth] => 1
        )

)

If you would Sort this one lets say on Section_Position it would return something like:

usort($sections, function($a, $b) {
     return $a['section_position'] - $b['section_position'];
});
  1. Section1
  2. SubSection1ForSection2
  3. SubSection1ForSection1
  4. Section2

Whilst I need it to order the Sections with their respective childs:

  1. Section1
  2. SubSection1ForSection1
  3. Section2
  4. SubSection1ForSection2

I assume Somehow Duplicate Question is the way to think but I can't seem to find a way to make this work for me.

Is there a way to do this or do I have to make a workaround with forloop-gets so that I first get all the values for depth one and then using the name of the section to get all the values of depth two and so forth?

(Sorry for my english.)

4
  • Possible duplicate of Sort Multi-dimensional Array by Value Commented Dec 12, 2016 at 11:14
  • Not quite the duplicate... I'm litarally telling that the answer from the possible duplicate isn't sufficiant... :) I need some more sorting like taking account of parents Commented Dec 12, 2016 at 11:15
  • Just a suggestion: check also stackoverflow.com/questions/17364127/… (linked by previous Q.) where there are many answers and some of them with more than 10 different types of multidimensional arrays sorting algorithms. Try also combining some ideas if there is no ready answer. Commented Dec 12, 2016 at 11:27
  • Thanks doing my best but I'm really not so good at figuring out algorithms and I was looking for an extra set of brains to help me out on this one :) I'm still doing research whilst waiting. Commented Dec 12, 2016 at 11:30

1 Answer 1

1

Okay this might be an ugly solution but if you put everything in a function it look good :). The good thing is that it will work in your scenario.

Code:

$inputData = array(
    array(
        'Section_Id' => 1,
        'Section_Name' => "Section1",
        'Section_Parent' => NULL,
        'Section_Position' => 1,
        'Section_Depth' => 0,
    ),
    array(
        'Section_Id' => 2,
        'Section_Name' => "Section2",
        'Section_Parent' => NULL,
        'Section_Position' => 0,
        'Section_Depth' => 0
    ),
    array(
        'Section_Id' => 4,
        'Section_Name' => "SubSection2ForSection2",
        'Section_Parent' => 2,
        'Section_Position' => 1,
        'Section_Depth' => 1
    ),
    array(
        'Section_Id' => 5,
        'Section_Name' => "SubSection1ForSection2",
        'Section_Parent' => 2,
        'Section_Position' => 0,
        'Section_Depth' => 1
    ),
    array(
        'Section_Id' => 3,
        'Section_Name' => "SubSection1ForSection1",
        'Section_Parent' => 1,
        'Section_Position' => 0,
        'Section_Depth' => 1
    )
);


$parentRecords = array();
$childRecords = array();
$sorted = array();

/* split in two collections */
foreach ($inputData as $sectionData) {
    if (is_null($sectionData['Section_Parent'])) {
        /* assume this is a parent */
        $parentRecords[] = $sectionData;
    } else {
        /* assume we are on child row */
        $childRecords[] = $sectionData;
    }
}

/* now first order parents by Section_Position */
usort($parentRecords, function($a, $b) {

        if ($a['Section_Position'] == $b['Section_Position']) {
            return 0;
        }
        return $a['Section_Position'] > $b['Section_Position'] ? 1 : -1;
    });

/* now the actual sorting */
foreach ($parentRecords as $parentData) {
    $parentId = $parentData['Section_Id'];
    /* now get all children of this parent */
    $parentChildren = array();
    foreach ($childRecords as $childData) {
        if ($childData['Section_Parent'] == $parentId) {
            $parentChildren[] = $childData;
        }
    }

    /* now sort the children by Section_Position */
    usort($parentChildren, function($a, $b) {

        if ($a['Section_Position'] == $b['Section_Position']) {
            return 0;
        }
        return $a['Section_Position'] > $b['Section_Position'] ? 1 : -1;
    });

    $sorted[] = $parentData;
    $sorted = array_merge($sorted, $parentChildren);
}


echo '<pre>' . print_r($sorted, true) . '</pre>';
exit;

OUTPUT:

Array
(
    [0] => Array
        (
            [Section_Id] => 2
            [Section_Name] => Section2
            [Section_Parent] => 
            [Section_Position] => 0
            [Section_Depth] => 0
        )

    [1] => Array
        (
            [Section_Id] => 5
            [Section_Name] => SubSection1ForSection2
            [Section_Parent] => 2
            [Section_Position] => 0
            [Section_Depth] => 1
        )

    [2] => Array
        (
            [Section_Id] => 4
            [Section_Name] => SubSection2ForSection2
            [Section_Parent] => 2
            [Section_Position] => 1
            [Section_Depth] => 1
        )

    [3] => Array
        (
            [Section_Id] => 1
            [Section_Name] => Section1
            [Section_Parent] => 
            [Section_Position] => 1
            [Section_Depth] => 0
        )

    [4] => Array
        (
            [Section_Id] => 3
            [Section_Name] => SubSection1ForSection1
            [Section_Parent] => 1
            [Section_Position] => 0
            [Section_Depth] => 1
        )

)

NOTE: first sort is done by respect to parents Section_Position and then to child's Section_Position

U P D A T E

First I want to say sorry to moderators for the long long discussion that we had with @Akorna but I needed to give him this code and I think it will do the job for the future. So @Akorna the code that should work for you is this one:

$inputData = array(
    array(
        'section_id' => 333,
        'section_depth' => 1,
        'section_parent' => 332,
        'section_position' => 0,
        'section_title' => 'Introduction'),
    array(
        'section_id' => 334,
        'section_depth' => 1,
        'section_parent' => 332,
        'section_position' => 1,
        'section_title' => 'Glossary'),
    array(
        'section_id' => 335,
        'section_depth' => 1,
        'section_parent' => 332,
        'section_position' => 2,
        'section_title' => 'Commands'),
    array(
        'section_id' => 336,
        'section_depth' => 1,
        'section_parent' => 332,
        'section_position' => 3,
        'section_title' => 'Components'),
    array(
        'section_id' => 337,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 0,
        'section_title' => 'Introduction'),
    array(
        'section_id' => 407,
        'section_depth' => 2,
        'section_parent' => 401,
        'section_position' => 2,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 338,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 1,
        'section_title' => 'AbstractContainer'),
    array(
        'section_id' => 406,
        'section_depth' => 2,
        'section_parent' => 401,
        'section_position' => 1,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 339,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 2,
        'section_title' => 'ActionsContainer'),
    array(
        'section_id' => 340,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 3,
        'section_title' => 'BrowserIncompatibility'),
    array(
        'section_id' => 404,
        'section_depth' => 2,
        'section_parent' => 402,
        'section_position' => 3,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 341,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 4,
        'section_title' => 'CollapsibleContainer'),
    array(
        'section_id' => 342,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 5,
        'section_title' => 'DetailsContainer'),
    array(
        'section_id' => 343,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 6,
        'section_title' => 'DynamicMenu'),
    array(
        'section_id' => 403,
        'section_depth' => 2,
        'section_parent' => 402,
        'section_position' => 1,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 344,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 7,
        'section_title' => 'Settings'),
    array(
        'section_id' => 345,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 8,
        'section_title' => 'SubfilesViewer'),
    array(
        'section_id' => 346,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 9,
        'section_title' => 'Taxonomy Management'),
    array(
        'section_id' => 402,
        'section_depth' => 1,
        'section_parent' => 400,
        'section_position' => 2,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 401,
        'section_depth' => 1,
        'section_parent' => 400,
        'section_position' => 1,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 347,
        'section_depth' => 2,
        'section_parent' => 336,
        'section_position' => 10,
        'section_title' => 'UploadQueue'),
    array(
        'section_id' => 400,
        'section_depth' => 0,
        'section_parent' => null,
        'section_position' => 5,
        'section_title' => 'Web Application'),
    array(
        'section_id' => 332,
        'section_depth' => 0,
        'section_parent' => null,
        'section_position' => 3,
        'section_title' => 'Web Application')
);

/* first order by section_depth and then by section_position */
$inputData = array_orderby($inputData, 'section_depth', SORT_ASC, 'section_position', SORT_ASC);

$parents = array();
$sortedByParent = false;
while (!$sortedByParent) {
    $elems = array_splice($inputData, count($inputData) - 1, 1);
    if (!count($elems)) {
        $sortedByParent = true;
        $inputData = array_merge($inputData, $parents);
        continue;
    }

    $elem = $elems[0];

    if ($elem['section_depth'] == 0) {
        if (!isset($elem['children'])) {
            $elem['children'] = array();
        }
        $parents[] = $elem;
    } else {
        $inputData = put_in_parent($elem, $inputData);
    }
}

/* now we have $inputData in nice format like
 * parent(child, child, child(child, child(child, child)), child(child(child(child)))),
 * parent(child, child, child(child, child(child, child)), child(child(child(child))))
 *  */
$inputData = merge_children_recursively(array_reverse($inputData));

function merge_children_recursively($inputData) {
    $children = array();
    foreach ($inputData as $row) {
        if (isset($row['children'])) {
            /* this ksort call is necessary because the key is actually section_position */
            ksort($row['children']);
            $rowCopy = $row;
            unset($rowCopy['children']);
            $children[] = $rowCopy;
            $children = array_merge($children, merge_children_recursively($row['children']));
        } else {
            $children[] = $row;
        }
    }

    return $children;
}

function put_in_parent($elem, $inputData) {
    foreach ($inputData as $k => $row) {
        if ($row['section_id'] == $elem['section_parent']) {
            if (!isset($inputData[$k]['children'])) {
                $inputData[$k]['children'] = array();
            }

            $inputData[$k]['children'][$elem['section_position']] = $elem;
            break;
        }
    }
    return $inputData;
}

function array_orderby() {
    $args = func_get_args();
    $data = array_shift($args);
    foreach ($args as $n => $field) {
        if (is_string($field)) {
            $tmp = array();
            foreach ($data as $key => $row) {
                $tmp[$key] = $row[$field];
            }
            $args[$n] = $tmp;
        }
    }
    $args[] = &$data;
    call_user_func_array('array_multisort', $args);
    return array_pop($args);
}

echo '<pre>' . print_r($inputData, true) . '</pre>';
exit;

I did remove some stuff from the input data so I could orient myself. Just try to give your input data to the logic and let me know what is the result.

Sign up to request clarification or add additional context in comments.

17 Comments

@Akorna but I really suggest you to avoid such things and better think about sorting the data with the SQL query with ORDER BY
The example you gave won't work for me, because the whole reason I'm doing this is because the Sections can actually be changed from order/depths (hence the Section Position and the Parent/child relations) This code is limited in the way that if you say that the first item suddenly stands last in row it will still put the child on first row with it. When ordering on position rather then ID, the parent will be gone but the child won't follow etc. I wanted to do it with the Order By but I just can't make the child relations like I'd want on that level and my acces to the DB is limited. :/
pastebin.com/xrfnC0hT This is the result I get from using your part but adapted to using the section_Position rather then Section_Id which doesn't represent the structure after an update. As you'll want to notice, the Folder-Childs aren't underneath their respective parent :(
@Akorna yes this is right because the sort is against Section_Id, i will shortly give you a kind of solution
@Akorna you can take the code from the UPDATE section. Cheers :)
|

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.