基于条件对数组进行排序

4

我有以下数组

$records = array(

    array("postId"=>"1","grid"=>"6"),
    array("postId"=>"2","grid"=>"3"),
    array("postId"=>"3","grid"=>"6"),
    array("postId"=>"4","grid"=>"3"),
    array("postId"=>"5","grid"=>"3"),
    array("postId"=>"6","grid"=>"12"),
    array("postId"=>"7","grid"=>"3"),

);

我希望能以一种方式对该数组进行排序,使得任何数量的连续的“网格”数之和均等于12。

例如:上述数组中“网格”的值为:6,3,6,3,3,12,3

(6+6=12), (3+3+3+3=12),(12=12) 因此新的顺序应为6,6,3,3,3,3,123,3,3,3,12,6,66,3,3,6,3,3,12

因此,在对数组进行排序后,新的数组应如下所示:

$records=array(

    array("postId"=>"1","grid"=>"6"),
    array("postId"=>"3","grid"=>"6"),
    array("postId"=>"2","grid"=>"3"),       
    array("postId"=>"4","grid"=>"3"),
    array("postId"=>"5","grid"=>"3"),
    array("postId"=>"7","grid"=>"3"),
    array("postId"=>"6","grid"=>"12"),

);

我在php手册中搜索了这些函数:sort,uasort,uksort,usort,但我不知道如何使用它们。
请问您能告诉我如何通过PHP实现排序吗?
更新:
网格(grid)的值始终为3或6或12(仅这三个数字)。
问题:
  $records = array(

    array("postId"=>"1","grid"=>"3"),
    array("postId"=>"2","grid"=>"6"),    
    array("postId"=>"3","grid"=>"3"),     
    array("postId"=>"4","grid"=>"3"),
    array("postId"=>"5","grid"=>"6"),
    array("postId"=>"6","grid"=>"6"),    
    array("postId"=>"7","grid"=>"3"),
    array("postId"=>"8","grid"=>"6"),

 );

1
这个问题并没有什么万能的解决办法,你需要从头开始编写一个相当复杂的函数。 - TravisO
“任意数量的连续的“网格”” => 这是什么意思? - Jon
@TravisO 不,顺序不一定是6,6,3,3,3,3,12。任何总和为12的顺序都可以。为了简化问题,我可以告诉你网格的值只会是3、6或12(仅限这三个数字)。 - black_belt
@SheikhHeera 是的,你说得对。但是数组不固定为3,3,3,3,6,6,12,它可能非常长。 - black_belt
@black_belt,“长度”不重要,对吧,你能给我一个可能得到的数组的例子吗? - The Alpha
显示剩余12条评论
4个回答

4
所以,你并不是真正地进行排序,而是重新排列来创建序列。我想你正在尝试使用固定高度的砖块进行一些布局,并且需要重新排序以填充每一行并将其余部分留在末尾。通过给定固定变量12,6,3,可以通过按降序排序来完成 - 如果有奇数个六,则会用较小的三来填充。然而,这种顺序会产生无聊的布局 - 为了使它更有趣,您只需要重新排序一些帖子。为此,您需要创建临时容器,并在其网格总和等于12时合并它。如果您还剩下一些临时容器,请将它们合并成一个,并在与先前分组合并之前按降序排序。
以下是说明我的概念的代码:
//auxiliary function to calculate sum of grids in given temporary container
    function reduc($a) {
    return array_reduce($a, function ($result, $item) {
        return $result . $item['grid'] . ',';
    }, '');
}

function regroup($records, $group_sum = 12) {
    $temp = array();
    $grouped = array();

    foreach ($records as $r) {
        if ($r['grid'] == $group_sum) {
            $grouped[] = $r;
        } else {
            if (!$temp) {
                $temp[] = array($r);
            } else {
                $was_grouped = false;
                foreach ($temp as $idx => $container) {
                    $current_sum = sum_collection($container);
                    if ($current_sum + $r['grid'] <= $group_sum) {
                        $temp[$idx][] = $r;
                        if ($current_sum + $r['grid'] == $group_sum) {
                            $grouped = array_merge($grouped, $temp[$idx]);
                            unset($temp[$idx]);
                        }
                        $was_grouped = true;
                        break;
                    }
                }
                if (!$was_grouped) {
                    $temp[] = array($r);
                }
            }
        }
    }

    if ($temp) {
        //Sort descending, so biggest ones will be filled first with smalller
        $rest = call_user_func_array('array_merge', $temp);
        usort($rest, function($a, $b) {
            return $b['grid'] - $a['grid'];
        });
        $grouped = array_merge($grouped, $rest);
    }

    return $grouped;
}

你是怎么知道我在尝试创建一个布局的?这正是我想做的 :) 。按降序排列确实会产生一个无聊的布局。你的代码非常智能,可以产生我想要的东西。也许我没有在我的问题中清楚地解释一切,但你以某种方式理解了它并为我开发了超级代码。你的代码不仅仅是按升序/降序排序,而是进行了真正的计算,找出了一个序列,可以用来创建一个漂亮的模板布局。我无法感谢你足够多。你帮了我很多忙。你是个天才。 :) - black_belt
请查看我的问题部分中的“问题”部分。如果我使用问题部分中显示的数组,我会得到重复的值。谢谢。 - black_belt
好的,这只是一个草率的概念验证,出现了一些问题。我已经修复了它,但它并不是生产级别的代码,因此我鼓励你自己实现它 :) - dev-null-dweller

2
问题是:
“我想以一种方式对这个数组进行排序,使得任何数量的相邻‘网格’的总和等于12。”
您可以尝试使用usort来实现。
$records = array(
    array("postId"=>"1","grid"=>"6"),
    array("postId"=>"2","grid"=>"3"),
    array("postId"=>"3","grid"=>"6"),
    array("postId"=>"4","grid"=>"3"),
    array("postId"=>"5","grid"=>"3"),
    array("postId"=>"6","grid"=>"12"),
    array("postId"=>"7","grid"=>"3"),
);

您好,这句话的英译中文是:“您有数字3出现了34次,数字6出现了2次,数字12出现了1次。”
// Sort (ASC)
usort($records, function($a, $b) {
    return $a['grid'] - $b['grid'];
});

DEMO-1 (ASC)3+3+3+3=126+6=1212=12)。

(注:该文本中的“ASC”可能是某个特定领域或项目的缩写,需要根据上下文进一步理解。)
// Sort (DESC)
usort($records, function($a, $b) {
    return $b['grid'] - $a['grid'];
});

DEMO-2(描述)12=126+6=123+3+3+3=12)。

按升序排序后的输出:

Array (

[0] => Array
    (
        [postId] => 7
        [grid] => 3
    )

[1] => Array
    (
        [postId] => 5
        [grid] => 3
    )

[2] => Array
    (
        [postId] => 4
        [grid] => 3
    )

[3] => Array
    (
        [postId] => 2
        [grid] => 3
    )

[4] => Array
    (
        [postId] => 3
        [grid] => 6
    )

[5] => Array
    (
        [postId] => 1
        [grid] => 6
    )

[6] => Array
    (
        [postId] => 6
        [grid] => 12
    )

)


1
是的,网格应该有12个。但您的比较并没有做到这一点,只是按数字从低到高排序。 - kero
1
是的,我想你是对的,但我确信这个答案并没有帮助。如果OP只对这个特定的数组感兴趣,他可以手动排序一次就完成了。拥有25k声望,你一定是在开玩笑 :) - Wolfgang Stengel
1
抱歉,实际上您只需要按降序排序即可获得我能想到的所有示例所需的结果。不知何故,我认为9也是可能的(因为它是3的倍数),但是OP明确排除了这一点。致敬。 - Wolfgang Stengel
1
@SheikhHeera非常感谢您的帮助,也感谢Wolfgang Stengel :) - black_belt
1
抱歉,这是意外发生的 :) - black_belt
显示剩余14条评论

1

这个解决方案首先按照网格大小降序排序,然后通过对每个剩余元素与每行目前的总和进行测试,以暴力方式逐步缩小范围:

$sum=0;
$grouped=array();
usort($records, function($a, $b) { return $a['grid']<$b['grid']; });
while ($records)
{
    $next=reset($records);
    if ($sum) foreach ($records as $next) if ($sum+$next['grid']<=12) break;
    $grouped[]=$next;
    $sum+=$next['grid'];
    unset($records[array_search($next, $records)]);
    if ($sum>=12) $sum=0;
}

更新

事实证明,按降序排序就足以使用仅有的3、6和12个元素来满足要求。一个12和一个6后跟一个6独立存在,所有其他组合都用剩余的三个填充。(由于某种原因,我认为算法还必须能够处理九。)所以这就是你所需要的全部内容:

usort($records, function($a, $b) { return $a['grid']<$b['grid']; });

虽然这样做会得到一个非常无聊的网格。


非常感谢您的回答。请问您能告诉我如何使用您的解决方案吗?因为 print_r($records); 没有给我任何结果 :) - black_belt
记录被放入$grouped中。 - Wolfgang Stengel
请想象一个这样的序列 6,3,3,3,6,6,如果按照我想要的方式进行排序,应该是这样的 6,6,3,3,6,36+6=12 3+3+6=12,3剩余),但是你的代码只是像这样排序 3,3,3,6,6,6 - black_belt

0
在 PHP >= 5.3.0 中,您可以使用 usort() 和闭包(或全局变量作为 hack)来实现此操作。给定 $records:
$running_length = 0;
usort( $records, function( $a, $b ) use( $running_length ) {
    $running_length += $a["grid"];
    if( $running_length >= 12 ) return( true );
    return( false );
});

如果您将“网格”参数视为字符串长度,则最终结果$records的顺序如下:
...            3
...            3
......         6
...                3
......             6
...                3
............  12

考虑到可用块的随机性,您可能希望首先将此数组从小到大排序,然后查看它是否更好地排列。显然,这种方法无法检测到碎片和不适合的块,也无法解决这些问题。


这到底是怎么工作的?;-) 你不应该知道数组元素传递到比较函数的顺序吗? - Wolfgang Stengel

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