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.)

Akorna
  • 217
  • 2
  • 16
  • Possible duplicate of [Sort Multi-dimensional Array by Value](http://stackoverflow.com/questions/2699086/sort-multi-dimensional-array-by-value) – Daniele D. Dec 12 '16 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 – Akorna Dec 12 '16 at 11:15
  • Just a suggestion: check also http://stackoverflow.com/questions/17364127/how-can-i-sort-arrays-and-data-in-php (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. – Daniele D. Dec 12 '16 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. – Akorna Dec 12 '16 at 11:30

1 Answers1

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.

codtex
  • 6,128
  • 2
  • 17
  • 34
  • @Akorna but I really suggest you to avoid such things and better think about sorting the data with the SQL query with `ORDER BY` – codtex Dec 12 '16 at 13:39
  • 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. :/ – Akorna Dec 12 '16 at 13:43
  • http://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 Dec 12 '16 at 13:49
  • @Akorna yes this is right because the sort is against Section_Id, i will shortly give you a kind of solution – codtex Dec 12 '16 at 13:52
  • Sadly if we're basing on Section_Id, the way I'm doing things I could just leave it untouched without ever changing anything to the order. Thing is, client would be able to change their mind and move the Sections but alot of data is bound to it, so deleting them or forcefully changing their ID would break alot of relations, hence why I need to find a way to order them correctly... – Akorna Dec 12 '16 at 14:09
  • @Akorna I did edit the whole post, because the code wasn't going to work for your use case. Please just refresh the page, code should work in respect of Section_Position field. Cheers – codtex Dec 12 '16 at 14:09
  • Did someone tell you you're a hero? – Akorna Dec 12 '16 at 14:26
  • It's going to get way more ugly because sadly child elements can get their own childs... :') – Akorna Dec 12 '16 at 14:34
  • What is going to be the data structure, we could think of a recursive algorith if it goes on deeper level ... Is a child of child going to have it's Section_Parent with the parentchild Section_Id, pfiuuu it is getting complicated – codtex Dec 12 '16 at 14:41
  • So basicly what's happeninig is we upload .md documents to mySQL(local) and SQL database(live) and then read those out on a website to display them. Each chapter of the document is a Section, those can have subtitles = childs of the sections, which can again have childs (infinitly basicly...). This is my first time ever attempting to solve something like this ^^. And so the section_depth represents in which child layer it is, the section_position is the ordening he has between childs on the same level, and all childs have the parent above as value, so not the root but directly above. – Akorna Dec 12 '16 at 14:43
  • I know I'm being a pain in the bu** :p – Akorna Dec 12 '16 at 14:46
  • http://pastebin.com/44gky625 So basicly in this one, 18 Is root, 19 -> 22 are children of 18 and 23 ->33 are children of 22. – Akorna Dec 12 '16 at 14:56
  • And what should be the sort order Parent then Child then ChildChild then ChildChildChild etc Parent Child ChildChild ... – codtex Dec 12 '16 at 15:00
  • Just like a real document so yes: Parent - Child - ChildOfChild - ChildOfChildOfChild || Parent2- Child - ChildOfChild, ChildOfChild2 etc. – Akorna Dec 12 '16 at 15:02
  • @Akorna this sounds like recursion I will try to come up with something – codtex Dec 12 '16 at 15:03
  • 1
    @Akorna you can take the code from the UPDATE section. Cheers :) – codtex Dec 12 '16 at 22:21
  • This worked like a charm, a million thanks seriously helped me out :o – Akorna Dec 13 '16 at 07:53