Sorting multidim array: prioritize if column contains substring, then order by a second column

前端 未结 6 687
情歌与酒
情歌与酒 2020-11-27 22:53

I am currently creating a sorting method that consists of values from an mysql query.

Here\'s a brief view of the array:

    Array
    (
        [0]          


        
6条回答
  •  时光取名叫无心
    2020-11-27 23:25

    I am currently creating a sorting method that consists of values from an mysql query.

    TRUTH:
    Using anything other than MySQL to sort your result set will be less efficient (with php, a usort() or array_multisort() call will be more convoluted and harder to maintain) and therefore inappropriate.

    SQL: (Demo)

    ORDER BY IF(LOCATE('EN', countries), 0, 1), id;
    

    This prioritizes countries column values that contain EN then sorts on id ASC.


    For anyone who isn't handling a sql result set or cannot manipulate the query for some reason, I endorse usort(). PHP7 offers a beautiful new operator that performs a comparison and returns one of three values (-1, 0, 1). This operator is affectionately called the "spaceship operator" and looks like this <=>.

    PHP: (Demo)

    $test = [
        ['id' => 1, 'countries' => 'EN,CH,SP'],
        ['id' => 2, 'countries' => 'GE,SP,SV'],
        ['id' => 3, 'countries' => 'PR,SP,IT'],
        ['id' => 4, 'countries' => 'EN'],
        ['id' => 5, 'countries' => 'SP,EN'],
        ['id' => 6, 'countries' => 'SV,SP,EN'],
        ['id' => 7, 'countries' => 'GE,SP'],
        ['id' => 8, 'countries' => 'FR'],
        ['id' => 9, 'countries' => 'RU,EN'],
        ['id' => 10, 'countries' => 'EN,SP,IT'],
        ['id' => 11, 'countries' => 'SP,GR'],
        ['id' => 12, 'countries' => 'GR,EN']
    ];
    
    usort($test, function($a, $b) {
        return [strpos($a['countries'], 'EN') === false, $a['id']] <=> [strpos($b['countries'], 'EN') === false, $b['id']];
    });
    
    var_export($test);
    

    Output:

    array (
      0 => 
      array (
        'id' => 1,
        'countries' => 'EN,CH,SP',
      ),
      1 => 
      array (
        'id' => 4,
        'countries' => 'EN',
      ),
      2 => 
      array (
        'id' => 5,
        'countries' => 'SP,EN',
      ),
      3 => 
      array (
        'id' => 6,
        'countries' => 'SV,SP,EN',
      ),
      4 => 
      array (
        'id' => 9,
        'countries' => 'RU,EN',
      ),
      5 => 
      array (
        'id' => 10,
        'countries' => 'EN,SP,IT',
      ),
      6 => 
      array (
        'id' => 12,
        'countries' => 'GR,EN',
      ),
      7 => 
      array (
        'id' => 2,
        'countries' => 'GE,SP,SV',
      ),
      8 => 
      array (
        'id' => 3,
        'countries' => 'PR,SP,IT',
      ),
      9 => 
      array (
        'id' => 7,
        'countries' => 'GE,SP',
      ),
      10 => 
      array (
        'id' => 8,
        'countries' => 'FR',
      ),
      11 => 
      array (
        'id' => 11,
        'countries' => 'SP,GR',
      ),
    )
    

    The array elements on either side of the spaceship operator are evaluated from left to right (leftside [0] vs rightside [0], then moving onto the pair of [1] values if there is a "tie" between the two [0] values).

    If the === false looks backwards, let me explain...

    If EN is found in the countries string, the condition will evaluate as false. When comparing true and false, remember that true equates to 1 and false equates to 0. We want ASC sorting, so we want to put false outcomes before true outcomes, ergo strings containing EN need to return false. Hopefully that clears up the logic.

提交回复
热议问题