7

I have the following array:

$array = [
    'note' => [],
    'year' => ['2011','2010', '2012'],
    'type' => ['conference', 'journal', 'conference'],
];

And I use the following function to sort the array using the field type and another array:

function array_multisort_by_order(array $array, $by, array $order)
{
    $order = array_flip($order);
    $params[] = $array[$by];
    foreach($params[0] as &$v) $v = $order[$v];
    foreach($array as &$v) $params[] = &$v; unset($v);
    call_user_func_array('array_multisort', $params);
    return $array;
}

When I call the following function I get the following error:

$array = array_multisort_by_order($array, 'type', array('conference', 'journal'));

print_r($array['type']);

Error:

Warning: array_multisort(): Array sizes are inconsistent.

I know that arrays are inconsistent. Is there a better function to use?

Please check: codepad

Desired Output:

Array
(
[note] => Array
    (
        [0] => 
        [1] => 
        [2] => 
    )

[year] => Array
    (
        [0] => 2011
        [1] => 2012
        [2] => 2010
    )

[type] => Array
    (
        [0] => conference
        [1] => conference
        [2] => journal
    )

)

Example 2:

Array

$array = [
    'note' => ['test1', 'test2'],
    'year' => ['2011', '2012'],
    'type' => ['conference', 'journal', 'conference'],
];

Desired Result 2

Array
(
[note] => Array
    (
        [0] => test1
        [1] => 
        [2] => tes2
    )

[year] => Array
    (
        [0] => 2011
        [1] => 2012
        [2] => 
    )

[type] => Array
    (
        [0] => conference
        [1] => conference
        [2] => journal
    )

)
4
  • 3
    Hm, desired output would make things more clear then quessing it from the code. Could you provide that? Commented Jun 19, 2012 at 20:31
  • OK, 1 question left: are the subarrays always either empty or of consistent length, or can we expect a subarray with 2 items here, and if so, how do we deal with that? Assume they still match up with the first 2 items from the other subarrays? Commented Jun 19, 2012 at 20:40
  • @Wrikken: A working example can be found here: codepad.org/mjSBYEyi . Unfortunately, sub-arrays are always inconsistent. I will provide another example. Added another example Commented Jun 19, 2012 at 20:42
  • OK, so numerical keys matter, got it. Commented Jun 19, 2012 at 20:48

2 Answers 2

3

OK, so, one of the first solutions that comes to mind is adding in the empty values to make them consistent:

function array_multisort_by_order(array $array, $by, array $order)
{
     $max = max(array_map('count',$array));
    //or, alternatively, depending on input (if there are no 'complete' subarrays):
    //$max = max(array_map(function($arr){return max(array_keys($arr));},$array))+1;

    //ADDITION: negative numeric keys:
    $min = min(array_map(function($arr){return min(array_keys($arr));},$array));
    $width = $max - min(0,$min);

    foreach($array as &$sub){
        // $addin = array_diff_key(array_fill(0,$max,null),$sub);
        // $addin changed for negative keys:
        $addin = array_diff_key(array_combine(range($min,$max),array_fill(0,$width,null)),$sub);
        $sub = $addin + $sub;
        ksort($sub);
    }
    $order = array_flip($order);
    $params[] = $array[$by];
    foreach($params[0] as &$v) $v = $order[$v];
    foreach($array as &$v) $params[] = &$v; unset($v);
    call_user_func_array('array_multisort', $params);
    //no closeures here:
    //foreach($array as &$sub) $sub = array_filter(function($a){return !is_null($a);},$sub);
    $filter = create_function('$a','return !is_null($a);');
    foreach($array as &$sub) $sub = array_filter($sub,$filter);
    return $array;
}
Sign up to request clarification or add additional context in comments.

6 Comments

Yes mate I thought of that. And I did codepad.org/4QcAoemv but it is not actually adding the keys to the original array. Additionally is there a way to get rid of those 'empty' values after doing the multisort?
Ugh, codepad is running < 5.3, cannot use closures... Something like this you mean: codepad.org/5kMHlRc6
Yes that will do the job. If you can check the code I tried to use a simpler method to fill missing keys foreach ($array['type'] as $k => $v) { foreach($array as $element => $a) { $iterator = $array[$element]; if(!isset($iterator[$k])){ $iterator[$key] = ''; } } }. This was temporary filling the $iterator array and not the original array. Thanks again for your help I will use your solution. I marked it as correct. Can you please edit your answer?
Edited in the final function.
There ya go, I hope you get the idea, if there is as error, feel free to edit the solution it in, I'm off ;)
|
0
  1. Pad the rows to have consistent element counts.
  2. Generate a lookup array using your custom sorting criteria, then populate the first sorting parameter with these translated values.
  3. Push all rows as reference variables into the payload of sorting arguments.
  4. Sort with array_multisort(). No return is needed because all sorting actions are done via references.

Code: (Demo)

function array_multisort_custom(array &$array, $columnKey, array $order)
{
    // guard clause/condition
    if (!array_key_exists($columnKey, $array)) {
        throw new Exception('Nominated sorting column not found');
    }
    
    // pad rows to consistent size
    $maxCount = max(array_map('count', $array));
    array_walk($array, fn(&$row) => $row = array_pad($row, $maxCount, null));

    // populate first sorting parameter with custom order array
    $priority = array_flip($order);
    $default = count($order);
    foreach ($array[$columnKey] as $v) {
        $params[0][] = $priority[$v] ?? $default;
    }
    
    // assign reference variables to parameter array for all rows
    foreach ($array as &$row) {
        $params[] = &$row;
    }
    
    array_multisort(...$params);
}

array_multisort_custom($array, 'type', ['conference', 'journal']);
var_export($array);

If the input array's first level keys were numeric, the above snippet can be compacted a little more and the last foreach() removed (because numeric keys can be unpacked with the spread operator inside of array_multisort()). Demo or even Demo

Related answer: Sort array using array_multisort() with dynamic number of arguments/parameters/rules/data

Comments

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.