5

我目前正在创建一个排序方法,其中包含来自 mysql 查询的值。

这是数组的简要视图:

    Array
    (
        [0] => Array
            (
                ['id'] = 1;
                ['countries'] = 'EN,CH,SP';
            )
        [1] => Array
            (
                ['id'] = 2;
                ['countries'] = 'GE,SP,SV';
            )
    )

我已经成功地根据数字 id 值进行了正常的排序,但我更想按“国家”字段的内容对数组进行排序(如果它包含一个设置的字符串,在这种情况下是一个国家代码),然后通过 id 字段。

下面的代码片段是我对如何做的第一个想法,但我不知道如何将它合并到一个工作函数中:

in_array('EN', explode(",",$a['countries']) );

你会怎么做?

谢谢!


不幸的是,我真的无处可去。

这是我目前所拥有的,它只给了我错误:uasort() [function.uasort]: Invalid comparison function

function compare($a, $b) {
    global $usercountry;

        if ( in_array($usercountry, $a['countries']) && in_array($usercountry, $a['countries']) ) {
            $return = 0;
        }

        else if (in_array($usercountry, $a['countries'])) {
            $return = 1;
        }

        else {
            $return = -1;
        }

        return $return;


        }

        $array= usort($array, "compare");

有没有人可以给我一个提示如何继续它?

4

4 回答 4

12

就个人而言,我会结合使用自定义(匿名)函数usort()

编辑:重新 - 你的评论。希望这将使您走上正确的道路。此功能对同时具有 EN 或都不具有 EN 的元素给予同等优先级,或者当只有一个具有 EN 时调整优先级。

usort($array,function ($a, $b) {
    $ac = strpos($a['countries'],'EN');
    $bc = strpos($b['countries'],'EN');
    if (($ac !== false && $bc !== false) || ($ac == false && $bc == false)) {
        return 0;
    }
    elseif ($ac !== false) {
        return 1;
    }
    else {
        return -1;
    }
});

另一方面,如果两者都有 EN,则此功能给予相同的优先级,如果一个有 EN,则优先级更高,如果两者都没有 EN,则进行文本比较。

usort($array,function ($a, $b) {
    $ac = strpos($a['countries'],'EN');
    $bc = strpos($b['countries'],'EN');
    if ($ac !== false && $bc !== false)) {
        return 0;
    }
    elseif ($ac !== false) {
        return 1;
    }
    elseif ($bc !== false) {
        return -1;
    }
    else {
        if ($a['countries'] == $b['countries']) {
            return 0;
        }
        elseif($a['countries'] > $b['countries']) {
            return 1;
        }
        else {
            return -1;
        }
    }
});

再次,希望这会给你足够的方向,让你自己前进。如果您有任何问题,请随时发表更多评论,我会尽力提供帮助。 如果您要将多个属性与重量进行比较,请注意:尝试一个时髦的开关块,例如

$ac = array_flip(explode(',',$a['countries']));
$bc = array_flip(explode(',',$b['countries']));
switch (true) {
    case array_key_exists('EN',$ac) && !array_key_exists('EN',$bc):
        return 1;
    case array_key_exists('DE',$ac) && !array_key_exists('EN',$bc) && !array_key_exists('EN',$bc):
        return 1;
    // and so on
}

更多编辑!

其实,我在复杂排序的问题上思考的比较多,我想出了下面的解决方案,供大家参考。它将允许您根据出现在国家/地区索引中的关键字定义数字排名。这是代码,包括一个示例:

示例数组

$array = array(
    array(
        'countries' => 'EN,DE,SP',
    ),
    array(
        'countries' => 'EN,CH,SP',
    ),
    array(
        'countries' => 'DE,SP,CH',
    ),
    array(
        'countries' => 'DE,SV,SP',
    ),
    array(
        'countries' => 'EN,SP,FR',
    ),
    array(
        'countries' => 'DE,FR,CH',
    ),
    array(
        'countries' => 'CH,EN,SP',
    ),

);

分拣程序

$rankings = array(
    'EN' => 10,
    'SP' => 8,
    'FR' => 7,
    'DE' => 5,
    'CH' => 3,
    'SV' => 1,
);
usort($array, function (&$a, &$b) use ($rankings) {
    if (isset($a['_score'])) {
        $aScore = $a['_score'];
    }
    else {
        $aScore = 0;
        $aCountries = explode(',',$a['countries']);
        foreach ($aCountries as $country) {
            if (isset($rankings[$country])) {
                $aScore += $rankings[$country];
            }
        }
        $a['_score'] = $aScore;
    }

    if (isset($b['_score'])) {
        $bScore = $b['_score'];
    }
    else {
        $bScore = 0;
        $bCountries = explode(',',$b['countries']);
        foreach ($bCountries as $country) {
            if (isset($rankings[$country])) {
                $bScore += $rankings[$country];
            }
        }
        $b['_score'] = $bScore;
    }
    if ($aScore == $bScore) {
        return 0;
    }
    elseif ($aScore > $bScore) {
        return -1;
    }
    else {
        return 1;
    }
});

注意:此代码会将排名最高的整体排序到数组的顶部。如果您想要反向行为,请更改此:

    elseif ($aScore > $bScore) {

    elseif ($aScore < $bScore) {

请注意,大于号已更改为小于号。进行此更改将导致排名最低的条目被排序到数组的顶部。希望这一切都有帮助!

还要注意!

此代码将对您的数组进行小幅更改,因为它将 _score 元素添加到每个数组。希望这不是问题,因为通过存储这个值,我确实能够将速度提高一倍以上(在我的基准测试中,0.00038-.00041 下降到 0.00016-.00018)。如果没有,则删除if检索缓存值的块,并让else块的内容每次都执行,当然存储分数值的部分除外。

顺便说一下,var_export()这是排序后的数组转储:

array (
  0 => array (
    'countries' => 'EN,SP,FR',
    '_score' => 25,
  ),
  1 => array (
    'countries' => 'EN,DE,SP',
    '_score' => 23,
  ),
  2 => array (
    'countries' => 'EN,CH,SP',
    '_score' => 21,
  ),
  3 => array (
    'countries' => 'CH,EN,SP',
    '_score' => 21,
  ),
  4 => array (
    'countries' => 'DE,SP,CH',
    '_score' => 16,
  ),
  5 => array (
    'countries' => 'DE,FR,CH',
    '_score' => 15,
  ),
  6 => array (
    'countries' => 'DE,SV,SP',
    '_score' => 14,
  ),
)

享受!

于 2010-01-13T18:50:31.570 回答
2

终于在 PHP.net 找到了这个奇妙的功能:

        function array_msort($array, $cols)
        {
            $colarr = array();
            foreach ($cols as $col => $order) {
                $colarr[$col] = array();
                foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
            }
            $eval = 'array_multisort(';
            foreach ($cols as $col => $order) {
                $eval .= '$colarr[\''.$col.'\'],'.$order.',';
            }
            $eval = substr($eval,0,-1).');';
            eval($eval);
            $ret = array();
            foreach ($colarr as $col => $arr) {
                foreach ($arr as $k => $v) {
                    $k = substr($k,1);
                    if (!isset($ret[$k])) $ret[$k] = $array[$k];
                    $ret[$k][$col] = $array[$k][$col];
                }
            }
            return $ret;

        }

这是每个国家的样子: $array['countries'] = in_array($needle, $haystack); }

$array = $array = array_msort($array, array('countries'=>SORT_DESC, 'id'=>SORT_ASC));

感谢你的帮助!

于 2010-01-13T22:30:56.680 回答
1

您可能会考虑array_walkand array_walk_recursivearray_map当它们结合在一起时,可能会做您想做的事情。

于 2010-01-13T18:43:59.263 回答
0

我目前正在创建一个排序方法,其中包含来自 mysql 查询的值。

真相:
使用 MySQL 以外的任何方法对结果集进行排序都会降低效率(使用 php,ausort()array_multisort()调用会更复杂,更难维护),因此是不合适的。

SQL:(演示

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

这会优先考虑countries包含EN然后按idASC 排序的列值。


对于没有处理 sql 结果集或由于某种原因无法操作查询的任何人,我赞同usort(). PHP7 提供了一个漂亮的新运算符,它执行比较并返回三个值之一(-1、0、1)。这个操作员被亲切地称为“飞船操作员”,长这样<=>

PHP:(演示

$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);

PHP8 替代方案:

return [str_contains($b['countries'], 'EN'), $a['id']]
       <=>
       [str_contains($a['countries'], 'EN'), $b['id']];

或者

return str_contains($b['countries'], 'EN') <=> [str_contains($a['countries'], 'EN')
       ?: $a['id']] <=> $b['id']];

输出:

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',
  ),
)

spaceship 运算符两侧的数组元素从左到右进行评估(左侧 [0] 与右侧 [0],然后如果两个 [0] 之间存在“平局”,则移动到 [1] 值对值)。

如果=== false向后看,让我解释一下...

如果EN在国家字符串中找到 ,则条件将评估为false。比较trueand时false,请记住它true等于 1 和false0。我们想要 ASC 排序,所以我们希望将错误结果放在真实结果之前,因此包含EN 需要返回 false 的字符串。希望这能澄清逻辑。

于 2019-05-29T23:00:16.937 回答