Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
385 views
in Technique[技术] by (71.8m points)

php - Sorting with a modulus

I am trying trying to sort a list into columns with uksort.

The array already alpha sorted, so it is like array('A','B','C','D','E','F','G','H','I','J','K','L','M')

Which gets displayed in html, as floated elements:

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

I want it reordered so it displays like this:

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

So the sorted array would be: array('A','E','H','K','B','F','I','L','C','G','J','M','D'

Basically, the same as Sorting a list alphabetically with a modulus but for php. I've tried taking the solution for javascript and convert it into php, but I'm not getting something right. Anyone have any ideas of how to do this in php?

This is what I have tried:

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");
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Setting up the following:

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

print_matrix($array, $columns);

Which outputs each member and it's key by index (row and colum) and as well the elements order on top:

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] 

The javascript code linked could be easily converted to PHP. However, if you look closely to that question/answer, it becomes clear that it only work with full rows, like with my previous attempt:

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

Output:

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] 

So it's just that the function provided in the other question does not work.

But as the array is already sorted, you don't need to sort it again but just to change the order or elements. But which order? If the matrix is not complete e.g. n x n fully filled, per each column, a different new index needs to be calculated. Taken the example with 13 elements (A-M) gives you the following distribution of rows per column:

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

So per each column, the value differs. For example at index 12, the 13th element is in the 4th row. On the way coming to that position, it has been passed 4 times through column 1 and 3 times in the other columns 2-4. So to get the virtual index of the iterated index, you need so sum how often you've been in each column to find out how many numbers in the original index you were going forward. If you go over the maximum number of members, you continue over at 0.

So this could be iteratively solved by stepping forward per each index to distribute the calculation over the indexes:

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

... and so on. If the virtual index goes over 12, it will start at 0, for example for the 5th Element (index 4) the virtual index would calculate 13:

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

Now filling a new array by starting with the virtual index 0 and giving the appropriate offset each time (look in which column you are, add the number of rows of that column, wrap around if necessary) will give the desired output:

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] 

Written in code, that's a simple foreach over the original indexes. By maintaining an index of keys as well, this works with any array, even those with string keys:

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

And that's it: Demo, Gist.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...