Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/236.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何通过交替“排序”对关联数组进行排序;性别“;价值_Php_Arrays_Sorting_Alternating - Fatal编程技术网

Php 如何通过交替“排序”对关联数组进行排序;性别“;价值

Php 如何通过交替“排序”对关联数组进行排序;性别“;价值,php,arrays,sorting,alternating,Php,Arrays,Sorting,Alternating,我有一个数组: $students= array( array("name"=>"...", "gender"=>"male"), array("name"=>"...", "gender"=>"female"), array("name"=>"...&q

我有一个数组:

$students= array(
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"male"),
);
我想按性别交替对
$students
元素进行排序,以获得:

$students= array(
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"male"),
    array("name"=>"...", "gender"=>"female"),
    array("name"=>"...", "gender"=>"male"),
);

我如何做到这一点?

我通过将元素过滤到两个单独的数组(
$male
$male
)来做到这一点
array\u filter
保留键,因此我们只需将其传递给
array\u values
即可获得从0开始的新键列表。从这里开始,它是一个简单的for循环,将它们交织在一起并添加到最终的数组中

<?php

$students= [
    ["name"=>"...", "gender"=>"male"],
    ["name"=>"...", "gender"=>"female"],
    ["name"=>"...", "gender"=>"female"],
    ["name"=>"...", "gender"=>"female"],
    ["name"=>"...", "gender"=>"male"],
    ["name"=>"...", "gender"=>"male"],
    ["name"=>"...", "gender"=>"male"],
];

$males = array_values(array_filter($students, function($s) { return $s["gender"] === "male"; }));
$females = array_values(array_filter($students, function($s) { return $s["gender"] === "female"; }));

$final = [];
$max = max(count($females), count($males));

for ($i=0; $i<$max; $i++) {
    if (isset($males[$i])) {
        $final[] = $males[$i];
    }

    if (isset($females[$i])) {
        $final[] = $females[$i];
    }
}

print_r($final);
原始解决方案
您可以使用根据性别创建两个组。然后使用将组压缩成对,并运行对以展平结构:

$males = array_filter($students, function ($e) {
    return $e["gender"] === "male";
});
$females = array_filter($students, function ($e) {
    return $e["gender"] === "female";
});
$zipped = array_map(null, $males, $females);
$result = array_reduce($zipped, function ($a, $e) {
    if ($e[0]) $a[] = $e[0];
    if ($e[1]) $a[] = $e[1];
    return $a;  
}, []);
时间复杂度为O(n)


减少开销

如果第一个解决方案开销太大,考虑消除函数调用。两次传递仍然是O(n),但分支预测应处理合并循环中性别之间数量严重不平衡的情况:

foreach ($students as $student) {
    if ($student["gender"] === "male") {
        $males[]= $student;
    }
    else {
        $females[]= $student;
    }
}

$male_count = count($males);
$female_count = count($females);

for ($i = 0, $j = 0; $i < $male_count || $j < $female_count;) {
    if ($i < count($males)) {
        $result[]= $males[$i++];
    }

    if ($j < count($females)) {
        $result[]= $females[$j++];
    }
}
输出:

array (
  0 => 
  array (
    'k' => 2,
  ),
  1 => 
  array (
    'k' => 1,
  ),
  2 => 
  array (
    'k' => 3,
  ),
  3 => 
  array (
    'k' => 2,
  ),
  4 => 
  array (
    'k' => 1,
  ),
  5 => 
  array (
    'k' => 3,
  ),
  6 => 
  array (
    'k' => 2,
  ),
  7 => 
  array (
    'k' => 1,
  ),
  8 => 
  array (
    'k' => 2,
  ),
)

Array\u filter将遍历数组,只需拆分一个数组就可以遍历两次。
而是使用foreach循环并拆分它。
然后用偶数或非偶数键组合每个部分,并将两者合并

foreach($students as $stu){
    if($stu['gender'] == 'male'){
        $male[] = $stu;
    }else{
        $female[] = $stu;
    }
}


$male = array_combine(range(0,(count($male)-1)*2,2),$male); // make keys even starting with 0
$female = array_combine(range(1,count($female)*2,2),$female); // make keys uneven starting with 1
$all = array_replace($male, $female); // replace can be used since they keys do not create any problems
ksort($all); //sort on key
$all = array_values($all);

var_dump($all);


另一种方法是在foreach中分配键,然后只进行数组替换。
这应该更快,因为涉及的数组函数更少

$i = 0;
$j = 1;
foreach($students as $stu){
    if($stu['gender'] == 'male'){
        $male[$i] = $stu;
        $i +=2;
    }else{
        $female[$j] = $stu;
        $j +=2;
    }
}

$all = array_replace($male, $female);
ksort($all);
$all = array_values($all);

var_dump($all);

我不建议创建临时的性别特定数组,然后合并它们

我喜欢维护两个性别特定计数器的效率,并且只在单个循环中迭代它们。我的循环不进行内部函数调用

事实上,与新的关键任务相比,决定哪种性别应该排在第一位需要更多的处理

代码:()


所以你想“分割”排序,以便每秒钟发生一次交换?你需要一个自定义的排序功能来。。。尽管我猜想,将所有男性先排序或将所有女性先排序会更有意义。如果男性和女性的数量不相等怎么办?我认为“拆分”是解决方案的一部分,因为我无法通过排序函数找到解决方案。你没有找到问题的解决方案并不意味着它不存在。唯一的困难是只有你知道问题是什么——如果不知道问题所在,就很难找到解决方案。这听起来确实像是一个。如果男性和女性的数量不相等,那么它就像:男性-女性-男性-女性-女性-女性使用
array\u map()
很有趣。很好的选择。谢谢,但我意识到它和你的差不多。我只是希望php能简写那些讨厌的lambda函数
function(){return$bleh;}
太难看了。谢谢ggorlen和FrankerZ,在一个简单的执行时间基准测试之后,我认为FrankerZ方法是最快的。如果你想要速度,我会完全跳过
array\u filter
并一直使用原始循环。TraveOff是相当好的代码而不是稍微快一点的代码,但是不管复杂度是O(n),因此它可能是一个过早的优化,这取决于您的用例。关于
过滤器
,您是对的,但不管通过多少次,至少总的时间复杂度是O(n)。但是使用
ksort
将复杂性提高到O(n log(n))。我还没有做过任何基准测试,但我想这会有一些n。确定你没有忘记2个x数组_值,它们会再遍历数组两次吗?我没有使用
array_值
,但从时间复杂度的角度来看,这是不相关的——去掉常数,你就会得到O(n)。我的错,这是另一个答案,
ksort
arsort
将时间复杂度从O(n)增加到O(n log(n))。第一个片段和第二个片段无条件地将男性放在第一位,导致潜在的连续女性条目。我的答案增加了时间复杂性的原因是为了更好地区分性别。如果有人付钱给我来降低我答案的时间复杂度,我可能会找到一种方法,但这就是我当天提出的提供准确结果的方法。OP。尽管如此,我认为您提出了一个优点,即最好的交错将是常见情况,因此我更新了代码并将其推广到任意值。但是,没有理由对这种情况下的时间复杂度进行分解——仔细看代码,
arsort
是可以的,因为它与唯一值的数量有关,并且是不可避免的,但是
ksort
是瓶颈。
$i = 0;
$j = 1;
foreach($students as $stu){
    if($stu['gender'] == 'male'){
        $male[$i] = $stu;
        $i +=2;
    }else{
        $female[$j] = $stu;
        $j +=2;
    }
}

$all = array_replace($male, $female);
ksort($all);
$all = array_values($all);

var_dump($all);
$students = [
    ["name" => "...", "gender" => "male"],
    ["name" => "...", "gender" => "female"],
    ["name" => "...", "gender" => "female"],
    ["name" => "...", "gender" => "female"],
    ["name" => "...", "gender" => "male"],
    ["name" => "...", "gender" => "male"],
    ["name" => "...", "gender" => "male"],
];

$counters = ['female' => 1, 'male' => 1];

// determine which gender should start from 0
$genderCounts = array_count_values(
    array_column($students, 'gender')
);
arsort($genderCounts);
--$counters[key($genderCounts)];

// assign keys
$result = [];
foreach ($students as $student) {
    $gender = $student['gender'];
    $result[$counters[$gender]] = $student;
    $counters[$gender] += 2;
}

ksort($result);

var_export($result);