patrick
patrick

Reputation: 302

Custom sort an associative array by keys

I need to sort an array in a custom way. Currently the array is set up as such:

array(3) {
  ["appetizer"]=>
  array(1) {
    ["slug"]=>
    string(9) "appetizer"
  }
  ["dessert"]=>
  array(1) {
    ["slug"]=>
    string(7) "dessert"
  }
  ["main"]=>
  array(1) {
    ["slug"]=>
    string(4) "main"
  }

I need to change the order of the array from alphabetical (its current order) to a custom order.

Current it is this: app/dessert/main.

And I need it to be app/main/dessert.

Not sure exactly how to update the array to the new custom order. I've messed with uksort() but didn't really get too far.

Here is the then function that generates the current array and its order (alphabetical)

function sort_dish_terms($term = array()){
    $tax = get_terms( $term ) ;
    $data = array();
    foreach ($tax as $t => $k) {
        // $name = $k->name;
        $slug = $k->slug;   
        $data[$slug] = array(
            'slug'  => $slug
        );  
    }
    return $data;
}

Upvotes: 1

Views: 870

Answers (3)

mickmackusa
mickmackusa

Reputation: 48031

Because your first level keys are all known and always exist, just flip your ordering array and replace its values with the real values.

Code: (Demo)

$order = ['appetizer', 'main', 'dessert'];

$items = [
    'appetizer' => ['slug' => 'appetizer'],
    'dessert' => ['slug' => 'dessert'],
    'main' => ['slug' => 'main']
];
var_export(
    array_replace(array_flip($order), $items)
);

Output:

array (
  'appetizer' => 
  array (
    'slug' => 'appetizer',
  ),
  'main' => 
  array (
    'slug' => 'main',
  ),
  'dessert' => 
  array (
    'slug' => 'dessert',
  ),
)

Upvotes: 0

Rob W
Rob W

Reputation: 9142

This is a much more elegant solution in my opinion, uses only 3 lines to do the actual sorting (or, it could be one if you'd like). Use PHP's uksort:

<?php

$priorities = array(
    "appetizer" => 1,
    "main"      => 2,
    "dessert"   => 3
);

$array = array(
    "appetizer" => array(
        "slug" => "appetizer"
    ),

    "dessert" => array(
        "slug" => "dessert"
    ),

    "main" => array(
        "slug" => "main"
    )
);

var_dump($array);

uksort($array, function($a, $b) use($priorities) {
    return $priorities[$a] > $priorities[$b];
});

var_dump($array);

?>

Will result in:

array(3) {
  ["appetizer"]=>
  array(1) {
    ["slug"]=>
    string(9) "appetizer"
  }
  ["dessert"]=>
  array(1) {
    ["slug"]=>
    string(7) "dessert"
  }
  ["main"]=>
  array(1) {
    ["slug"]=>
    string(4) "main"
  }
}
array(3) {
  ["appetizer"]=>
  array(1) {
    ["slug"]=>
    string(9) "appetizer"
  }
  ["main"]=>
  array(1) {
    ["slug"]=>
    string(4) "main"
  }
  ["dessert"]=>
  array(1) {
    ["slug"]=>
    string(7) "dessert"
  }
}

Working example: http://codepad.viper-7.com/OeMLhD

Upvotes: 1

AnotherGuy
AnotherGuy

Reputation: 615

I would solve this by determine the desired order before generating the array. Something like the following:

$priorities = [
    'appetizer',
    'main',
    'dessert'
];

/*
 * The $items array is generated by the function you provided.
 */
$items = [
    'appetizer' => [
        'slug' => 'appetizer'
    ],
    'dessert' => [
        'slug' => 'dessert'
    ],
    'main' => [
        'slug' => 'main'
    ]
];

I have then created a function that can be used in such cases.

function sort_by_priorities(array $priorities, array $items) {

    /*
     * Check if the two arrays are populated.
     */
    if(empty($priority) || empty($items)) {
        throw new LogicException('Parameters $priorities and $items cannot be empty.');
    }

    $sorted = [];

    /*
     * Ensure all array keys are lowercase for better comparison.
     */
    $items = array_change_key_case($items, CASE_LOWER);

    foreach($priorities as $priority) {

        $priority = strtolower($priority);

        if(array_key_exists($priority, $items)) {

            $sorted[$priority] = $items[$priority];

        }

    }

    return $sorted;

}

Then call the function and you should receive the desired order.

$sorted = sort_by_priorities($priorities, $items);

This will provide an array like the following using var_dump():

array (size=3)
  'appetizer' => 
    array (size=1)
      'slug' => string 'appetizer' (length=9)
  'main' => 
    array (size=1)
      'slug' => string 'main' (length=4)
  'dessert' => 
    array (size=1)
      'slug' => string 'dessert' (length=7)

This can be extended if you wish. The current version of this function doesn't care if there are an equal amount of array elements in both arrays, something that may or may not be useful to you.

Hope this helps.

Upvotes: 0

Related Questions