Home > Software design >  How to sort array by month abbreviation
How to sort array by month abbreviation

Time:11-19

I have a multidimensional array that I'm trying to sort by the 3-letter month abbreviation, here's what I have tried:

The array: $shipping_chart_month =

[
  {
    "name": "8:00 AM",
    "data": [
        {
          "x": "May",
          "y": 37
        },
        {
          "x": "Nov",
          "y": 32
        },
        {
          "x": "Apr",
          "y": 1
        },
        {
          "x": "Aug",
          "y": 45
        },
        {
          "x": "Sep",
          "y": 19
        },
        {
          "x": "Jul",
          "y": 13
        },
        {
          "x": "Oct",
          "y": 43
        },
        {
          "x": "Jun",
          "y": 31
        },
        {
          "x": "Feb",
          "y": 0
        },
        {
          "x": "Jan",
          "y": 0
        },
        {
          "x": "Mar",
          "y": 0
        }
      ]
    },
    {
      "name": "9:00 AM",
      "data": [
        {
          "x": "Apr",
          "y": 26
        },
        {
          "x": "Oct",
          "y": 84
        },
        {
          "x": "Sep",
          "y": 35
        },
        {
          "x": "Jul",
          "y": 26
        },
        {
          "x": "Feb",
          "y": 6
        },
        {
          "x": "Nov",
          "y": 96
        },
        {
          "x": "Mar",
          "y": 10
        },
        {
          "x": "May",
          "y": 50
        },
        {
          "x": "Aug",
          "y": 66
        },
        {
          "x": "Jun",
          "y": 36
        },
        {
          "x": "Jan",
          "y": 0
        }
      ]
    }
]

I'm trying to sort each sub data array by Month. Here's what I've tried but it doesn't seem to have any affect and stuck on where to go from here:

 foreach ($shipping_chart_month as $data) {
     $months = [];
     foreach ($data['data'] as $month) {
         $months[] = $month['x'];
     }
     usort($months, function ($x, $y) {
         $months = array('Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12);
         if ($months[$x] == $months[$y]) {
             return 0;
         }
         return ($months[$x] > $months[$y]) ? 1 : -1;
     });
 }

CodePudding user response:

Starting with your data in a JSON string, which is what you've presented above:

$shipping = '[
  {
      "name": "8:00 AM",
    "data": [
        {
            "x": "May",
          "y": 37
        },
        {
            "x": "Nov",
          "y": 32
        },
        {
            "x": "Apr",
          "y": 1
        },
        {
            "x": "Aug",
          "y": 45
        },
        {
            "x": "Sep",
          "y": 19
        },
        {
            "x": "Jul",
          "y": 13
        },
        {
            "x": "Oct",
          "y": 43
        },
        {
            "x": "Jun",
          "y": 31
        },
        {
            "x": "Feb",
          "y": 0
        },
        {
            "x": "Jan",
          "y": 0
        },
        {
            "x": "Mar",
          "y": 0
        }
      ]
    },
    {
        "name": "9:00 AM",
      "data": [
        {
            "x": "Apr",
          "y": 26
        },
        {
            "x": "Oct",
          "y": 84
        },
        {
            "x": "Sep",
          "y": 35
        },
        {
            "x": "Jul",
          "y": 26
        },
        {
            "x": "Feb",
          "y": 6
        },
        {
            "x": "Nov",
          "y": 96
        },
        {
            "x": "Mar",
          "y": 10
        },
        {
            "x": "May",
          "y": 50
        },
        {
            "x": "Aug",
          "y": 66
        },
        {
            "x": "Jun",
          "y": 36
        },
        {
            "x": "Jan",
          "y": 0
        }
      ]
    }
]';

// json_decode() the data so we can work with it.
$shipping_chart_month = json_decode($shipping);


// Loop through the array of objects, looking at the $data property in each one
foreach($shipping_chart_month as $chart) {

    // usort() sorts in place, so apply it to the $data array
    usort($chart->data, function($a, $b){
        $months = array('Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12);
        // We use the $months array to swap from a month abbreviation to a 
        // numeric value.
        // We compare the index of the x property of the objects in the $data array
        // the spaceship operator compares and returns -1, 0, 1 as appropriate 
        return $months[$a->x]<=>$months[$b->x];
    });
}

// Output the results.
print_r($shipping_chart_month);

CodePudding user response:

You was close.
The month alias map (Jan => 1, ...) was a good idea.
You just needed to loop though all arrays in the main array
and sort them (by reference).

Note: im using uasort, but you could also use usort.

The source array:

$shipping_chart_month = [
    [
        "name" => "8=>00 AM",
        "data" => [
            [
                "x" => "May",
                "y" => 37
            ],
            [
                "x" => "Nov",
                "y" => 32
            ],
            [
                "x" => "Apr",
                "y" => 1
            ],
            [
                "x" => "Aug",
                "y" => 45
            ],
            [
                "x" => "Sep",
                "y" => 19
            ],
            [
                "x" => "Jul",
                "y" => 13
            ],
            [
                "x" => "Oct",
                "y" => 43
            ],
            [
                "x" => "Jun",
                "y" => 31
            ],
            [
                "x" => "Feb",
                "y" => 0
            ],
            [
                "x" => "Jan",
                "y" => 0
            ],
            [
                "x" => "Mar",
                "y" => 0
            ]
        ]
    ],
    [
        "name" => "9=>00 AM",
        "data" => [
            [
                "x" => "Apr",
                "y" => 26
            ],
            [
                "x" => "Oct",
                "y" => 84
            ],
            [
                "x" => "Sep",
                "y" => 35
            ],
            [
                "x" => "Jul",
                "y" => 26
            ],
            [
                "x" => "Feb",
                "y" => 6
            ],
            [
                "x" => "Nov",
                "y" => 96
            ],
            [
                "x" => "Mar",
                "y" => 10
            ],
            [
                "x" => "May",
                "y" => 50
            ],
            [
                "x" => "Aug",
                "y" => 66
            ],
            [
                "x" => "Jun",
                "y" => 36
            ],
            [
                "x" => "Jan",
                "y" => 0
            ]
        ]
    ]
];

The sorting:

// The month alias map.
$monthAliasMap = array(
    // {month alias} => {sort priority (lower before higher)}
    'Jan' => 1,
    'Feb' => 2,
    'Mar' => 3,
    'Apr' => 4,
    'May' => 5,
    'Jun' => 6,
    'Jul' => 7,
    'Aug' => 8,
    'Sep' => 9,
    'Oct' => 10,
    'Nov' => 11,
    'Dec' => 12,
);
// Note: using each $array in $shipping_chart_month by reference.
foreach ($shipping_chart_month as &$array) {
    uasort($array['data'], function ($a, $b) use ($monthAliasMap) {
        // $a | $b example:
        // array (
        //     'x' => 'May',
        //     'y' => 37,
        // )

        // Set the offset we expect the month alias on.
        $offset = 'x';
        // Get the month alias from a and b.
        $aMonthAlias = $a[$offset]; // F.e. "Jan" or "Dec" ...
        $bMonthAlias = $b[$offset]; // F.e. "Jan" or "Dec" ...
        // Get (map) the sort priority for a and b.
        $aPriority = (int)$monthAliasMap[$aMonthAlias]; // F.e. 1 or 2 ...
        $bPriority = (int)$monthAliasMap[$bMonthAlias]; // F.e. 1 or 2 ...

        // Sort them.
        if ($aPriority === $bPriority) {
            return 0;
        }
        // a < b === asc ; a > b === desc
        return ($aPriority < $bPriority) ? -1 : 1;
    });

}
unset($array); // Release reference.

Result (shortened):

[
    0 => [
        'name' => '8=>00 AM',
        'data' => [
            9 => [
                'x' => 'Jan',
                'y' => 0,
            ],
            8 => [
                'x' => 'Feb',
                'y' => 0,
            ],
            // ...
            1 => [
                'x' => 'Nov',
                'y' => 32,
            ],
        ],
    ],
    1 => [
        'name' => '9=>00 AM',
        'data' => [
            10 => [
                'x' => 'Jan',
                'y' => 0,
            ],
            4  => [
                'x' => 'Feb',
                'y' => 6,
            ],
            // ...
            5  => [
                'x' => 'Nov',
                'y' => 96,
            ],
        ],
    ],
];
  • Related