简体   繁体   中英

Sort By Alphabet then Numbers Laravel Collection

I am looking for a way to sort the collection in such a way that name values starting with the alphabet comes at the top and then name values that start with numbers. For example:

$collection = collect([
    ['name' => 'b', 'symbol' => '#'],
    ['name' => '2a', 'symbol' => '$'],
    ['name' => '1', 'symbol' => '@'],
    ['name' => 'a', 'symbol' => '%']
]);

The above collection should be sorted like this:

[
    [
        "name" => "a",
        "symbol" => "%",
    ],
    [
        "name" => "b",
        "symbol" => "#",
    ],
    [
        "name" => "1",
        "symbol" => "@",
    ],
    [
        "name" => "2a",
        "symbol" => "$",
    ],
]

But this is what I get when I sort it using sortBy method:

$collection->sortBy('name')->values()->all();
[
    [
        "name" => "1",
        "symbol" => "@",
    ],
    [
        "name" => "2a",
        "symbol" => "$",
    ],
    [
        "name" => "a",
        "symbol" => "%",
    ],
    [
        "name" => "b",
        "symbol" => "#",
    ],
]

Any idea how to sort this collection so that names starting with letters come first?

You need to define your own custom comparator function to sort these collection objects using sort .

Compare both names by checking they are all alphabets. If both are alphabets, then usual string comparison using strcasecmp shall suffice. If either of them is an alphabet, push them to higher ranks by returning value -1 , meaning to be placed above in the sorted order. If both are numerical or alphanumeric, use strcasecmp again.

<?php

$collection = collect([
    ['name' => 'b', 'symbol' => '#'],
    ['name' => '2a', 'symbol' => '$'],
    ['name' => '1', 'symbol' => '@'],
    ['name' => 'a', 'symbol' => '%']
]);

$collection = $collection->sort(function($a,$b){
    $a_is_alphabet = preg_match('/^[a-zA-Z]+$/', $a['name']) === 1;
    $b_is_alphabet = preg_match('/^[a-zA-Z]+$/', $b['name']) === 1;

    if($a_is_alphabet && $b_is_alphabet){
        return strcasecmp($a['name'], $b['name']);
    }elseif($a_is_alphabet){
        return -1;
    }elseif($b_is_alphabet){
        return 1;
    }

    return strcasecmp($a['name'], $b['name']);
});

You want purely alphabetical name values to have top priority, then I assume natural sorting so that, say a2 comes before a10 . Just write two rules in a custom callback in a sort() method call.

False evaluations are ordered before true evaluations when sorting ASC, so merely write the $b element before the $a element to sort DESC. To break any ties on the first comparison, call strnatcmp() .

Laravel adopted arrow function syntax back in 2019.

Code: ( Basic PHP Demo )

$collection->sort(fn($a, $b) =>
    (ctype_alpha($b['name']) <=> ctype_alpha($a['name']))
    ?: strnatcmp($a['name'], $b['name'])
);

If you, more specifically only want to check if the first character is a letter, you can use $a['name'][0] and $b['name'][0] . If the strings might have a multi-byte first character then a regex approach might be best .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM