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

发布于 2024-08-17 22:20:59 字数 1209 浏览 11 评论 0原文

我目前正在创建一个由 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");

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

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] => Array
            (
                ['id'] = 1;
                ['countries'] = 'EN,CH,SP';
            )
        [1] => Array
            (
                ['id'] = 2;
                ['countries'] = 'GE,SP,SV';
            )
    )

I have succeeded in making a normal usort based on the numeric id values, but I rather want to sort the array by the content of the "countries" field (if it contains a set string, a country code in this case), and then by the id field.

The following snippet was my first idea of how to do it, but I have no idea of how to incorporate it into an working function:

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

How would you do it?

Thanks!


I am really getting nowhere with this unfortunately.

Here is what I have for the moment, and its giving me nothing but errors: 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");

Is there anyone who might give me a hint of how to go on with it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

紅太極 2024-08-24 22:20:59

就我个人而言,我会结合使用自定义(匿名)函数 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 元素添加到每个数组中。希望这不是问题,因为通过存储这个值,我实际上能够将速度提高一倍以上(在我的基准测试中,速度从 .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,
  ),
)

享受吧!

Personally, I would use a custom (anonymous) function in conjunction with usort().

EDIT: Re - your comment. Hopefully this will put you on the right track. This function gives equal priority to elements which both have EN or neither have EN, or adjusted priority when just one has 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;
    }
});

This function, on the other hand, gives equal priority if both have EN, higher if one has EN, and does a text comparison if neither has 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;
        }
    }
});

Again, hopefully this will give you enough direction to move forward on your own. If you are having any problems, feel free to post more comments and I'll try to help. A note if you're tying to compare multiple properties with weight: try out a funky switch block, 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
}

#More Edits!#
Actually, I was thinking more on the problem of complex sorting, and I have come up with the following solution, for your consideration. It will allow you to define numerical rankings based on keywords which would appear in the countries index. Here is the code, including an example:

Example Array

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

);

Sorting Routine

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

Note: This code will sort the highest ranking entires to the top of the array. If you want reverse behavior, change this:

    elseif ($aScore > $bScore) {

to

    elseif ($aScore < $bScore) {

Note that the greater-than was changed to a less-than symbol. Making this change will result in the lowest ranking entries being sorted to the top of the array. Hope all this helps!

##NOTE ALSO!##
This code will make a small change to your array, in that it adds the _score element to each array. Hopefully this is not a problem, as by storing this value I was literally able to increase speed by more than double (.00038-.00041 down to .00016-.00018 in my benchmarks). If not, remove the if blocks that retrieve the cached value and let the contents of the else blocks execute every time, except of course for the part which stores the score value.

By the way, here's a var_export() dump of the array after it was sorted:

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

Enjoy!

迷你仙 2024-08-24 22:20:59

终于在 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));

大家的帮助!

Finally found this wonderful function at 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;

        }

This is how each country looks like:
$array['countries'] = in_array($needle, $haystack);
}

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

Thanks all for your help!

苏别ゝ 2024-08-24 22:20:59

您可能会考虑 array_walkarray_walk_recursivearray_map,组合在一起时可能会去做你想做的事。

You might consider array_walk and array_walk_recursive and array_map, which when combined together maybe to get to doing what you want to do.

呆° 2024-08-24 22:20:59

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

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

SQL: (演示)

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

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


对于不处理 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',
  ),
)

太空飞船运算符两侧的数组元素从从左到右(左侧 [0] 与右侧 [0],然后如果两个 [0] 值之间存在“平局”,则移至一对 [1] 值)。

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

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

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

PHP8 alternatives:

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

or

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文