对 multidim 数组进行排序:如果列包含子字符串,则优先排序,然后按第二列排序
-
20-09-2019 - |
题
我目前正在创建一个由 mysql 查询中的值组成的排序方法。
这是数组的简要视图:
Array
(
[0] => Array
(
['id'] = 1;
['countries'] = 'EN,CH,SP';
)
[1] => Array
(
['id'] = 2;
['countries'] = 'GE,SP,SV';
)
)
我已经成功地根据数字 id 值进行正常的 usort,但我更想按“countries”字段的内容对数组进行排序(如果它包含一组字符串,在本例中为国家/地区代码),然后通过 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");
有没有人可以给我一个提示,告诉我如何继续下去?
解决方案
个人而言,我使用自定义与 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;
}
}
});
再次希望这会给你足够的方向在自己的前进。如果您有任何问题,随意张贴更多的意见,我会尽力帮助。 如果你绑到多个性能与重量比的注意事项:尝试了一个时髦的开关块,e.g
$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;
}
});
注意:该代码将在最高排名entires排序到阵列强>的顶部。如果你想扭转行为,更改此:
elseif ($aScore > $bScore) {
到
elseif ($aScore < $bScore) {
请注意变更为小于符号的,大于。使这种变化将导致在排名最低的条目进行排序到阵列强>的顶部。希望这一切都有助于!
还要注意!
此代码将使一个小的变化,以您的阵列,因为它增加了_score元件到每个阵列。但愿这不是一个问题,因为通过存储这个值我从字面上能够通过增加一倍以上(0.00038,0.00041降至0.00016,0.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,
),
)
享受!
其他提示
最后发现这个精彩功能在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;
}
这是各个国家的样子: $阵列[ '国家'] = in_array($针,$草堆); }
$array = $array = array_msort($array, array('countries'=>SORT_DESC, 'id'=>SORT_ASC));
感谢所有您的帮助!
你可能会考虑 array_walk
和 array_walk_recursive
和 array_map
, ,当组合在一起时也许可以做你想做的事情。
与尝试 array_mulisort 。
查核 uasort 来看看如何使用用户定义的比较函数。
我目前创建包含值从MySQL查询分拣方法。
真相:点击使用MySQL之外的任何排序结果集的效率会降低(与PHP,一个usort()
或array_multisort()
通话将更加令人费解和难以维持),因此不恰当
SQL:(演示)
ORDER BY IF(LOCATE('EN', countries), 0, 1), id;
这个优先包含countries
然后各种各样上EN
ASC id
列值。
对于任何人谁不处理一个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);
输出:
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',
),
)
上宇宙飞船操作者两侧的数组元素从左到右(莱夫特赛德[0] VS rightside评价[0],然后移动到所述一对[1]的值,如果有一个“扎”在两者之间[0]的值)。
如果该=== false
看起来倒退,让我解释...
如果在各国家的串中发现EN
,条件将评估为false
。当比较true
和false
,请记住,true
相当于1 false
等于0。我们希望ASC排序,所以我们希望把假的结果之前真正的成果,包含EN
ERGO字符串的需要的返回false。希望这清除了的逻辑。