如何按“性别”值对关联数组进行排序?

时间:2019-08-19 02:44:47

标签: php arrays usort

我有一个数组数组:

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

我该怎么做?

4 个答案:

答案 0 :(得分:2)

我通过将元素过滤到2个单独的数组($males$females)中来做到这一点。 array_filter保留键,因此我们只需将其传递到array_values即可获得从0开始的新键列表。从那里开始,这是一个简单的for循环,可以将它们交织并添加到最终数组中。 / p>

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

请参见此演示here

答案 1 :(得分:1)

您可以使用array_filter根据性别创建两个组。然后使用array_map将各组压缩成对,并通过array_reduce运行该对以展平结构:

<?php
$students = [
    ["gender" => "male"],
    ["gender" => "female"],
    ["gender" => "female"],
    ["gender" => "female"],
    ["gender" => "male"],
    ["gender" => "female"],
    ["gender" => "female"],
    ["gender" => "male"],
    ["gender" => "male"],
    ["gender" => "female"],
    ["gender" => "female"],
];

$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;  
}, []);

print_r($result);

输出:

Array
(
    [0] => Array
        (
            [gender] => male
        )

    [1] => Array
        (
            [gender] => female
        )

    [2] => Array
        (
            [gender] => male
        )

    [3] => Array
        (
            [gender] => female
        )

    [4] => Array
        (
            [gender] => male
        )

    [5] => Array
        (
            [gender] => female
        )

    [6] => Array
        (
            [gender] => male
        )

    [7] => Array
        (
            [gender] => female
        )

    [8] => Array
        (
            [gender] => female
        )

    [9] => Array
        (
            [gender] => female
        )

    [10] => Array
        (
            [gender] => female
        )

)

如果您正在寻找纯速度,这应该可以解决问题。具有两次通过和分支预测的O(n)应该处理两种性别之间存在很大不平衡的情况(如果不是,则将最后一个循环分成三个;应该具有相同的性能):

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

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

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

答案 2 :(得分:1)

Array_filter会遍历数组,而不必只对数组进行两次遍历。
而是将其与foreach循环并拆分。
然后,使用偶数或不均匀的数字键将array_combine组合在一起,并将两者合并。

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

https://3v4l.org/TZCKN


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

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

https://3v4l.org/k3MMj

答案 3 :(得分:1)

我不建议您创建临时的针对性别的数组,然后将其拉链合并。

我喜欢维护两个针对性别的计数器并仅在一个循环中对其进行迭代的效率。我的循环不进行内部函数调用。

实际上,确定哪种性别应该先出现比新的键分配需要更多的处理。

代码:(Demo

$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);
相关问题