elimariaaa
elimariaaa

Reputation: 866

Sort array alphabetically by specific string

I have this array of plans:

Array
(
[0] => Array
    (
        [plan_id] => corporate-base
        [plan_name] => Tier 1 - NYC
    )

[1] => Array
    (
        [plan_id] => corporate-la-base
        [plan_name] => Tier 1 - LA
    )

[2] => Array
    (
        [plan_id] => corporate-sf-base
        [plan_name] => Tier 1 - SF
    )

[3] => Array
    (
        [plan_id] => corporate-core
        [plan_name] => Tier 2 - NYC
    )

[4] => Array
    (
        [plan_id] => corporate-la-core
        [plan_name] => Tier 2 - LA
    )

[5] => Array
    (
        [plan_id] => corporate-sf-core
        [plan_name] => Tier 2 - SF
    )

[6] => Array
    (
        [plan_id] => corporate-la-unlimited
        [plan_name] => Tier 3 - LA
    )

[7] => Array
    (
        [plan_id] => corporate-sf-unlimited
        [plan_name] => Tier 3 - SF
    )

[8] => Array
    (
        [plan_id] => corporate-unlimited
        [plan_name] => Tier 3 - NYC
    )

)

I want them to be grouped by city like:

Tier 1 - LA

Tier 2 - LA

Tier 3 - LA

Tier 1 - NYC

Tier 2 - NYC

Tier 3 - NYC

Tier 1 - SF

Tier 2 - SF

Tier 3 - SF

I tried to use array_multisort() but it displays all Tier 1 first then Tier 2 and then Tier 3.

Currently, I'm doing 3 foreach loops to group them into 3 cities but it's not flexible and I think it's too long. What could be the quickest/easiest way to achieve this? Thanks!

Upvotes: 0

Views: 39

Answers (1)

Nick
Nick

Reputation: 147146

You can use usort, using a sort function which extracts the city name out of the plan_name value using explode. If the city names are the same, we extract the tier number so we can sort by that instead:

usort($array, function ($a, $b) {
    $city_a = explode('-', $a['plan_name'])[1];
    $city_b = explode('-', $b['plan_name'])[1];
    if ($city_a == $city_b) {
        // sort by tier
        $tier_a = (int)explode(' ', $a['plan_name'])[1];
        $tier_b = (int)explode(' ', $b['plan_name'])[1];
        return $tier_a - $tier_b;
    }
    else {
        return strcmp($city_a, $city_b);
    }
});

I haven't included the output as it is quite long but you can see it in this demo on 3v4l.org

This is an alternate version which use preg_match to extract the city and tier from each plan_name:

usort($array, function ($a, $b) {
    preg_match('/^Tier\s*(\d+)\s*-\s*(\w+)$/', $a['plan_name'], $matches_a);
    preg_match('/^Tier\s*(\d+)\s*-\s*(\w+)$/', $b['plan_name'], $matches_b);
    // are the cities the same?
    if ($matches_a[2] == $matches_b[2]) {
        // yes, sort by tier
        return $matches_a[1] - $matches_b[1];
    }
    else {
        // no, sort by city
        return strcmp($matches_a[2], $matches_b[2]);
    }
});

Demo on 3v4l.org

Upvotes: 1

Related Questions