基于模数的排序

4

我正在尝试使用uksort将列表按列排序。该数组已经按字母顺序排序,例如array('A','B','C','D','E','F','G','H','I','J','K','L','M')

在HTML中,它会显示为浮动元素:

A B C D
E F G H
I J K L
M

我希望你能重新排列,使其显示如下:

我想让它按照这样的顺序显示:

A E H K
B F I L
C G J M
D

因此,排序后的数组为:array('A','E','H','K','B','F','I','L','C','G','J','M','D'

基本上,与使用模数按字母顺序排序列表相同,但用于php。我尝试将javascript的解决方案转换为php,但是我做错了什么。有人有想法如何在php中实现吗?

这是我尝试过的:

function cmp_nav_by4($a, $b) {
    if (($a % 5) < ($b % 5)) {
        return 1;
    } elseif (($a % 4) > ($b % 4)) {
        return -1;
    } else {
        return $a < $b ? 1 : -1;
    }
}
$result = uksort($thearray, "cmp_nav_by4");

2
数据在排序前如何存储? - Dvir
2
在大多数情况下,您应该在问题中包含您的代码尝试。 - Jared Farrish
这个数据是像你在这里展示的那样一个数组的数组吗?因为那样你只需要迭代它并交换即可。 - Incognito
它已经按字母顺序排序,因此它是一个单一的数组,例如array('A', 'B', 'C', 'D', 'E', 'F'等)。 - Echo says Reinstate Monica
那么你的意思是你有一个数组 A B C D E F G H I J K L,并且你想要一个数组 A D G J B E H K C F I L?也就是说,“列”和“行”的概念实际上在数据中并没有被表示出来?(如果是这样的话——你怎么知道有三行四列呢?) - ruakh
@ruakh 是的,我更新了我的问题以使其更清晰。我知道会有多少列,行数会有所变化。 - Echo says Reinstate Monica
2个回答

7

设置以下内容:

$array = range('A', 'M');
$columns = 4;
$length = count($array);

print_matrix($array, $columns);

该代码通过索引(行和列)输出每个成员及其键,以及元素顺序在顶部:

One row - A B C D E F G H I J K L M
A[ 0] B[ 1] C[ 2] D[ 3] 
E[ 4] F[ 5] G[ 6] H[ 7] 
I[ 8] J[ 9] K[10] L[11] 
M[12] 

连接的 JavaScript 代码可以很容易地转换为 PHP。但是,如果您仔细查看该问题/答案,就会发现它只适用于完整的行,就像我的先前尝试一样:
function callback_sort($array, $columns)
{
    $sort = function($columns)
    {
        return function($a, $b) use ($columns)
        {
            $bycol = ($a % $columns) - ($b % $columns);
            return $bycol ? : $a - $b;
        };
    };

    uksort($array, $sort(4));

    return $array;
}

输出:

One row - A E I M B F J C G K D H L
A[ 0] E[ 4] I[ 8] M[12] 
B[ 1] F[ 5] J[ 9] C[ 2] 
G[ 6] K[10] D[ 3] H[ 7] 
L[11] 

所以,其他问题中提供的函数无法使用。

但是由于数组已经排序,您不需要再次对其进行排序,而只需要改变元素的顺序。但是应该按照什么顺序呢?如果矩阵不完整,例如 n x n 没有完全填充,则需要针对每列计算不同的新索引。以包含13个元素(A-M)的示例为例,您可以得到以下每列的行分布:

column: 1 2 3 4
rows:   4 3 3 3

每一列的值都是不同的。举个例子,在索引12处,第13个元素位于第4行。在到达该位置的过程中,它已经通过第1列传递了4次,在其他2-4列中传递了3次。因此,为了获取迭代索引的虚拟索引,需要将每个列中出现的次数相加,以找出在原始索引中前进了多少个数字。如果超过成员最大数量,则从0继续。

因此,可以通过按每个索引向前步进来分配计算来迭代地解决这个问题:

Index 0:
    No column: 0

Index 1:
    1x in column is which has 4 rows: 4

Index 2:
    1x in column 1 (4 rows) and 1x in other columns (3 rows): 4 + 3

…等等。如果虚拟索引超过12,它将从0开始,例如对于第5个元素(索引4),虚拟索引将计算为13:

Index 4:
    1x 4 rows and 3x 3 rows = 13 (4 + 9)
    13 > 12 => 1 (13 - 12)

现在通过以虚拟索引0开始并每次给出适当的偏移量来填充一个新数组(查看您所在的列,添加该列的行数,必要时进行换行)将得到所需的输出:

One row - A E H K B F I L C G J M D
A[ 0] E[ 4] H[ 7] K[10] 
B[ 1] F[ 5] I[ 8] L[11] 
C[ 2] G[ 6] J[ 9] M[12] 
D[ 3] 

这段代码中,使用foreach语句遍历原始索引即可。同时,通过维护一个键的索引,可以使用在任何数组上进行操作,包括字符串关联数组:

$floor = floor($length/$columns);
$modulo = $length % $columns;
$max = $length-1;
$virtual = 0;
$keys = array_keys($array);
$build = array();
foreach($keys as $index => $key)
{
    $vkey = $keys[$virtual];
    $build[$vkey] = $array[$vkey];
    $virtual += $floor + ($index % $columns < $modulo);
    ($virtual>$max) && $virtual %= $max;
}

print_matrix($build, $columns);

就是这样:演示代码片段


完整的字母表(我认为这更清楚地展示了效果):http://codepad.org/OyYkexa6 - Jared Farrish
另外,我不知道这是否会对OP造成问题,但它也会修剪任何剩余的内容:http://codepad.org/L5QjyYJW 只是提供信息。 - Jared Farrish
@JaredFarrish 是的,修剪剩余的将是一个问题。我更新了我的问题。 - Echo says Reinstate Monica
正如迭代已经展示的那样,存在一个模式。该模式可以表示为一个公式,其中 b = f4(a)。这也解决了填充问题。 - hakre
@回声:原始的JavaScript函数仅适用于A-L示例,我已更改答案以显示它如何与A-M一起使用。 - hakre

1

@hakre有正确的代码答案。为什么:

底层排序函数Zend_qsort实际上并不重新排列元素和键。相反,它重新排列zend引擎使用的内部数组桶。如果您对数字索引数组进行ksort,然后使用$q = count($array);for($i=0; $i<$q); $i++)迭代,它将返回与之前完全相同的值;如果您使用for($key in $array)迭代,则会得到新的键排序。


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