对multidim数组进行排序:如果列包含子字符串,则优先级排序,然后按第二列排序

时间:2010-01-13 18:37:23

标签: php arrays multidimensional-array usort

我目前正在创建一个由mysql查询中的值组成的排序方法。

以下是该数组的简要视图:

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

我已经成功地根据数字id值进行了正常的操作,但是我更愿意按照“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");

是否有人可能会给我一些如何继续使用它的提示?

6 个答案:

答案 0 :(得分: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;
    }
});

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

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

享受!

答案 1 :(得分: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));

感谢大家的帮助!

答案 2 :(得分:1)

您可以考虑array_walkarray_walk_recursive以及array_map,这些内容组合在一起可能会让您做您想做的事情。

答案 3 :(得分:0)

尝试使用array_mulisort

答案 4 :(得分:0)

查看uasort以了解如何使用用户定义的比较功能。

答案 5 :(得分:0)

  

我目前正在创建一种排序方法,该方法由mysql查询中的值组成。

真相:
使用MySQL以外的任何东西对结果集进行排序将效率较低(对于php,usort()array_multisort()调用会更好复杂且难以维护),因此不合适。

SQL:(Demo

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

这会优先处理包含countries的{​​{1}}列值,然后在EN ASC上进行排序。


对于任何不处理sql结果集或由于某种原因无法操纵查询的人,我都支持id。 PHP7提供了一个漂亮的新运算符,该运算符执行比较并返回三个值(-1、0、1)之一。这个运算符亲切地称为“太空飞船运算符”,看起来像usort()

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

从左到右评估飞船运算符两侧的数组元素(左侧[0]与右侧[0],然后移动到一对[1]值,如果两者之间有“并列” [0]个值)。

如果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', ), ) 向后看,让我解释一下...

如果在国家/地区字符串中找到=== false,则条件的值为EN。在比较falsetrue时,请记住false等于1,而true等于0。我们要进行ASC排序,因此我们想将假结果放在真实结果之前,包含false 需要的ergo字符串返回false。希望这可以清除逻辑。