RhymeGuy
RhymeGuy

Reputation: 2122

Sort each subarray of a 2d array by delimited string values

Original array:

$resolutions =  array (
    "Desktop monitor" => array (
        //Standard 4:3
        "800x600" => "800x600",
        "1024x768" => "1024x768",
        "1600x1200" => "1600x1200",
        //Wide 16:10
        "960x600" => "960x600",
        "1280x800" => "1280x800",
        "1440x900" => "1440x900",
        "1680x1050" => "1680x1050",
        "1920x1200" => "1920x1200",
    ),
    "Apple" => array (
        "DeviceX" => "2048x1536",
        "DeviceY" => "1024x768",
    ),
);

Wanted array:

$resolutions =  array (
    "Desktop monitor" => array (
        "800x600" => "800x600",//Standard 4:3
        "960x600" => "960x600",//Wide 16:10
        "1024x768" => "1024x768",//Standard 4:3
        "1280x800" => "1280x800",//Wide 16:10
        "1440x900" => "1440x900",//Wide 16:10
        "1600x1200" => "1600x1200",//Standard 4:3
        "1680x1050" => "1680x1050",//Wide 16:10
        "1920x1200" => "1920x1200",//Wide 16:10
    ),
    "Apple" => array (
        "DeviceY" => "1024x768",
        "DeviceX" => "2048x1536",
    ),
);

What I have tried:

foreach ($resolutions as $screen => $resolution) { 
    foreach($resolution as $key => $val) {
        $newarray[$key] = $row[$val];
    }
    array_multisort($newarray, SORT_DESC, $resolution);
}

I thought that I'm half way to end, but the code above gave me some non sense (for me), ie: first goes resolution 1024x768, then 1280x800, followed by 1440x900 and in the end is 800x600.

Upvotes: 2

Views: 165

Answers (4)

mickmackusa
mickmackusa

Reputation: 48031

Because your sample dimension values always show the larger number before the x delimiter and there are no significantly lean or fat ratios, a key-preserving natural sort on each set will do the trick. Demo

array_walk(
    $resolutions,
    function (&$row) {
        asort($row, SORT_NATURAL);
    }
);
var_export($resolutions);

If your project has more squirrelly dimension values, then iterated calculations should be minimized. Demo

array_walk(
    $resolutions,
    function (&$row) {
        array_multisort(
            array_map(
                fn($v) => array_product(sscanf($v, '%dx%d')),
                $row
            ),
            $row
        );
    }
);
var_export($resolutions);

Upvotes: 0

scrowler
scrowler

Reputation: 24425

This is a quick hack looking specifically for the Desktop monitor array key. ksort will sort by array key, but it doesn't sort well when you have an x in the middle of some numbers. Here's my solution:

  • Loop through array
  • Build new array with only first half of resolution as array key
  • Use ksort to sort new array
  • array_walk new array to create output array which looks like original

You could do the same thing for other arrays inside your original array e.g. Apple by using the value instead of the key as the resolution, and put the array_walk etc inside a loop.

Upvotes: 0

Havenard
Havenard

Reputation: 27914

The problem is that you are sorting alphabetically. You will need to calculate the size of each resolution to sort them correctly. This will require writing a custom comparison function that calculate the resolution sizes and compare them.

foreach ($resolutions as &$resolution)
    uasort($resolution, function ($a, $b) { return array_product(explode('x', $a)) - array_product(explode('x', $b)); });

Upvotes: 1

user622327
user622327

Reputation:

Just looking at what you've put there, two things stand out:

  • You're looping through the outer array and sorting a single-dimensional array using multisort; I would recommend just using asort().
  • Not quite sure what's going on there with $resolutions being passed in as the thrid paramter. It wants an array of sort flags.

try:

foreach ($resolutions as $resolution) { 
        asort($resolution);
    }

Upvotes: 0

Related Questions