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