0

I need some help because I'm stuck. I've developed a HTML/JS/PHP sorting where users can move elements around or create child elements by moving an element inside another. As a result, I'm getting this array:

$sorted_ids = [
    '2', // <-- Value = Main element ID
    [
        '4' => [ // <-- Key = Main element ID
            '3' // <-- Value = Child element ID
        ]
    ]
    '1', // <-- Value = Main element ID
];

On the other side, I have my array of elements containing all of these above IDs:

$elements = [
    [
        'id' => '1',
        'name' => 'Test1'
    ],
    [
        'id' => '2',
        'name' => 'Test2'
    ],
    [
        'id' => '3',
        'name' => 'Test3'
    ],
    [
        'id' => '4',
        'name' => 'Test4'
    ]
];

I've used now the following code to loop through the array of sorted IDs:

foreach ( $sorted_ids as $sorted_id ) {
    if ( is_array( $sorted_id ) ) {
        foreach ( $sorted_id as $sorted_parent_id => $sorted_child_id ) {

        }
    } else {

    }
}

In my loop, I can now access all relevant IDs, but I really don't know how to sort the elements by its field ID. I've tried using array_shift() but always went wrong. Maybe there is a better idea?

At the end, my elements array need to look like this:

$elements = [
    [
        'id' => '2'
        'name' => 'Test2'
    ],
    [
        'id' => '4',
        'name' => 'Test4'
    ],
    [
        'id' => '3',
        'name' => 'Test3'
    ],
    [
        'id' => '1,
        'name' => 'Test1'
    ]
];

Update

I forgot to mention that my objects in the elements array can have multiple fields like name, field, place, value... (I just added name as an example), so just flatten the sorted IDs array might be a good idea, but problematic!

Mr. Jo
  • 4,946
  • 6
  • 41
  • 100

1 Answers1

1

The input array can be understood in a clearer manner if we include the keys in the output. A var_dump of the input array would look like this

array (size=3)
  0 => string '2' (length=1)
  1 => 
    array (size=1)
      4 => 
        array (size=1)
          0 => string '3' (length=1)
  2 => string '1' (length=1)

As per the question there are two inferences

  1. The input array is already sorted, so all we need to do is flatten it.
  2. The nesting levels is finite. If the levels of nesting was not known we would need a recursive function.

Therefore, all we need to do is iterate over the input array and assign the values of interest to the output array.

$elements = [
    [
        'id' => '1',
        'name' => 'Test1'
    ],
    [
        'id' => '2',
        'name' => 'Test2'
    ],
    [
        'id' => '3',
        'name' => 'Test3'
    ],
    [
        'id' => '4',
        'name' => 'Test4'
    ]
];
## Assuming 'id' is unique. We re-index the $elements array with id as key - makes it easier to access its elements.
$elementsArray = array_column($elements, null, 'id');

$sorted_ids = [
    '2', // <-- Value = Main element ID
    [
        '4' => [ // <-- Key = Main element ID
            '3' // <-- Value = Child element ID
        ]
    ],
    '1', // <-- Value = Main element ID
];
$results = [];

foreach ( $sorted_ids as $sorted_id ) {
    if ( is_array( $sorted_id ) ) {
        foreach ($sorted_id as $sorted_parent_id => $sorted_child_id_array) {
            $results[] = $elementsArray[$sorted_parent_id];
            ## IF more than one child we use foreach
            $results[] = $elementsArray[$sorted_child_id_array[0]];
        }
    } else {
        $results[] = $elementsArray[$sorted_id];
    }
}
var_dump($results);

The output for the above will be

array (size=4)
  0 => 
    array (size=2)
      'id' => string '2' (length=1)
      'name' => string 'Test2' (length=5)
  1 => 
    array (size=2)
      'id' => string '4' (length=1)
      'name' => string 'Test4' (length=5)
  2 => 
    array (size=2)
      'id' => string '3' (length=1)
      'name' => string 'Test3' (length=5)
  3 => 
    array (size=2)
      'id' => string '1' (length=1)
      'name' => string 'Test1' (length=5)

And the sandbox link with a working solution Sandbox link after the edit

As a footnote I must mention that you may want to redo the codes to create the nested array. In fact since the output is a flat array why even create a nested one. The input array can be created like this too

$sorted_ids = [
    [
        'id' => '1',
        'order' => 4
    ],
    [
        'id' => '2',
        'order' => 1
    ],
    [
        'id' => '3',
        'order' => 3
    ],
    [
        'id' => '4',
        'order' => 2
    ]
];

This would make it much easier to achieve what you want without the need for a loop. Whether this can be done or not depends on the codes creating the input array. My guess is to relook at the statement can move elements around or create child elements by moving an element inside another. As the output stands there is no parent child relationship maintained (which may be implemented in your codes) and hence we may even rephrase it to can move elements around which leaves us with a JS function to keep a track of the moved sort order. Again these are guesses since you have not shared those codes.

endeavour
  • 576
  • 4
  • 15
  • Awesome! One thing makes this function a bit problematic - my elements array can have multiple fields like id, name, place, value.... so in this case these values must be included somehow. I need to improve my question here - sorry! – Mr. Jo Jul 03 '21 at 09:35
  • The input array remain the same, does it? – endeavour Jul 03 '21 at 09:38
  • Maybe we can use your flattened `$result` array to do some kind of sorting with my `$elements` array. I'll do some research! – Mr. Jo Jul 03 '21 at 09:38
  • Yes, just the objects order should be changed accordingly to the value of the `id` field in each object by using your idea for a sorted flattened array. – Mr. Jo Jul 03 '21 at 09:40
  • Check this out: https://stackoverflow.com/questions/4282413/sort-array-of-objects-by-object-fields Maybe we can use your `$result` array now to get this working. If I find a way first, I'll send you the working script so that you can improve your answer. Lets see whos faster! – Mr. Jo Jul 03 '21 at 09:42
  • OK, however is the sorting not done by the users manually? _where users can move elements around or create child elements by moving an element inside another_ What is the need to again sort programmatically. – endeavour Jul 03 '21 at 09:44
  • Yes, it's done manually by using jQuery UI sortable. After the user is done, he clicks a button and I get my sorted id's in an array like shown above. – Mr. Jo Jul 03 '21 at 09:46
  • Hence, the array you get is already sorted and does not need to be sorted again. You may try to sort the $elements array based on this array but in my opinion to `loop` (as you are already doing) over the array you get and create a results array would be simpler and less error-prone – endeavour Jul 03 '21 at 09:48
  • 1
    Assigning values to the results array is a minor task – endeavour Jul 03 '21 at 09:50
  • Edited the answer to include the new `$elements` array. – endeavour Jul 03 '21 at 10:00
  • Wherever I say input array I actually mean the array `$sorted_ids`. If this is confusing, I can edit the answer description to rename `input array` to `$sorted_ids` – endeavour Jul 03 '21 at 10:35
  • And for what its worth, the $results array is actually the sorted `$elements` array (a copy of it actually) – endeavour Jul 03 '21 at 10:44
  • Awesome idea! I'll try it out later since I'm not on my computer and checkmark your answer! – Mr. Jo Jul 03 '21 at 13:01