多维数组排序:如果某列包含子字符串,则优先考虑该列,然后按第二列排序。

5

我正在创建一个排序方法,其中包含来自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");

有没有人能给我一些关于如何继续进行的提示?(涉及IT技术)

出于好奇,为什么不首先从数据库中将它们排序,例如 ORDER BY country, id - Gordon
它已经按ID排序了,正如你所说的那样,但这并不能解决问题中的in_array部分... - Industrial
可能是重复的问题,参考:PHP中对数组和数据进行排序的所有基本方法 - vzwick
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;
        }
    }
});

希望这次能给你足够的指引,让你能够自己继续前进。如果你遇到任何问题,随时发表更多评论,我会尽力帮助你。如果你想要比较多个属性的权重,请尝试使用一个有趣的switch块,例如。
$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元素。希望这不会成为问题,因为通过存储这个值,我成功地将速度提高了一倍以上(从.00038-.00041降到.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,
  ),
)

享受吧!


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); }
(说明:此代码段是一个PHP代码示例,用于检查一个值是否在数组中。它使用in_array函数来执行此操作。)
$array = $array = array_msort($array, array('countries'=>SORT_DESC, 'id'=>SORT_ASC));

感谢大家的帮助!

哎呀……eval()?完全不必要。 - mickmackusa

1

1
这个"hint"本可以作为问题下的评论。 - mickmackusa

0
我目前正在创建一个由mysql查询值组成的排序方法。 真相:使用除MySQL之外的任何内容对结果集进行排序都将效率低下(使用php,usort()array_multisort()调用将更加复杂和难以维护),因此不合适。
SQL:(演示
ORDER BY IF(LOCATE('EN', countries), 0, 1), id;

这将优先考虑包含 ENcountries 列值,然后按 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);

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

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

如果 === false 看起来很奇怪,让我解释一下...

如果在countries字符串中找到 EN ,则条件将评估为 false 。当比较 true false 时,请记住 true 等于1,而 false 等于0。我们想要ASC排序,因此我们希望将false结果放在true结果之前,因此包含 EN 的字符串需要返回false。希望这样可以澄清逻辑。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接