按多个字段对多维数组进行排序
Sort multidimensional array by multiple fields
我发现了一些关于此的问题,但我觉得我这里有一个非常特殊的案例,所以我要问一个新问题。
我需要先按用户(数组)的头衔对用户数组进行排序,然后按姓氏排序,请考虑以下代码:
<?php
$users = [
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
]
];
而我想要的顺序是:
<?php
$order = [
'Senior Manager',
'Manager',
'Supervisor'
];
如果有多个经理,他们应该按姓氏排序,所以在这种情况下输出将是:
<?php
$sorted = [
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
],
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
]
];
我已经按照这些思路尝试了一些方法,但无法正常工作,并且发现调试起来有点困难 usort
:
<?php
foreach ($order as $title) {
usort($users, function ($a, $b) use ($title) {
# Both have the title
if (in_array($title, $a['titles']) and in_array($title, $b['titles']) ) {
# Sort by name
return strcmp($a['lastName'], $b['lastName']);
}
# A has the title
elseif (in_array($title, $a['titles'])) {
return 1;
}
# B has the title
elseif (in_array($title, $b['titles'])) {
return -1;
}
# No-one has the title
return strcmp($a['lastName'], $b['lastName']);
});
}
问题是数组被一遍又一遍地排序。因此,如果您在 foreach 循环中逐步进行,项目将首先按高级经理职位排序,然后再次按经理职位排序,最后再次按主管职位排序。
所以首先,你必须颠倒你想要的顺序:
$order = array_reverse([
'Senior Manager',
'Manager',
'Supervisor'
]);
那么如果 none 个项目有标题,您就不想四处移动项目。因为在您的其他迭代中,您的 Senior Manager 可能没有 Supervisor 头衔,而其他用户也可能没有该头衔,因此他们都不应更改职位。所以你可以把最后的return改成return 0;
最后,您颠倒了 1
和 -1
的 return,因此您只需将它们调换即可。
最后,最终代码如下所示:
$users = [
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
]
];
$order = array_reverse([
'Senior Manager',
'Manager',
'Supervisor'
]);
foreach ($order as $title) {
usort($users, function ($a, $b) use ($title) {
if (in_array($title, $a['titles']) && in_array($title, $b['titles'])) {
return strcmp($a['lastName'], $b['lastName']);
}
# A has the title
elseif (in_array($title, $a['titles'])) {
return -1;
}
# B has the title
elseif (in_array($title, $b['titles'])) {
return 1;
}
return 0;
});
}
输出将是:
array(6) {
[0]=>
array(2) {
["lastName"]=>
string(5) "Adams"
["titles"]=>
array(2) {
[0]=>
string(7) "Manager"
[1]=>
string(14) "Senior Manager"
}
}
[1]=>
array(2) {
["lastName"]=>
string(6) "Davids"
["titles"]=>
array(1) {
[0]=>
string(14) "Senior Manager"
}
}
[2]=>
array(2) {
["lastName"]=>
string(6) "Clarks"
["titles"]=>
array(2) {
[0]=>
string(7) "Manager"
[1]=>
string(10) "Supervisor"
}
}
[3]=>
array(2) {
["lastName"]=>
string(8) "Clarkson"
["titles"]=>
array(1) {
[0]=>
string(7) "Manager"
}
}
[4]=>
array(2) {
["lastName"]=>
string(5) "Adams"
["titles"]=>
array(1) {
[0]=>
string(10) "Supervisor"
}
}
[5]=>
array(2) {
["lastName"]=>
string(8) "Clarkson"
["titles"]=>
array(1) {
[0]=>
string(5) "Sales"
}
}
}
您想要的是根据 $order
中用户头衔的 最低索引 对用户进行排序。您可以使用 array_search
在 $order
中找到他们的每个标题索引,并使用 min
找到最小的数字。如果它们相同,则退回到 strcmp
.
usort($users, function($a, $b) use ($order) {
$minAPos = min(array_map(function($title) use ($order) {
$pos = array_search($title, $order);
return $pos === false? sizeof($order) : $pos;
}, $a['titles']));
$minBPos = min(array_map(function($title) use ($order) {
$pos = array_search($title, $order);
return $pos === false? sizeof($order) : $pos;
}, $b['titles']));
if($minAPos === $minBPos) {
return strcmp($a['lastName'], $b['lastName']);
} else {
return $minAPos <=> $minBPos;
}
});
我发现了一些关于此的问题,但我觉得我这里有一个非常特殊的案例,所以我要问一个新问题。
我需要先按用户(数组)的头衔对用户数组进行排序,然后按姓氏排序,请考虑以下代码:
<?php
$users = [
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
]
];
而我想要的顺序是:
<?php
$order = [
'Senior Manager',
'Manager',
'Supervisor'
];
如果有多个经理,他们应该按姓氏排序,所以在这种情况下输出将是:
<?php
$sorted = [
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
],
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
]
];
我已经按照这些思路尝试了一些方法,但无法正常工作,并且发现调试起来有点困难 usort
:
<?php
foreach ($order as $title) {
usort($users, function ($a, $b) use ($title) {
# Both have the title
if (in_array($title, $a['titles']) and in_array($title, $b['titles']) ) {
# Sort by name
return strcmp($a['lastName'], $b['lastName']);
}
# A has the title
elseif (in_array($title, $a['titles'])) {
return 1;
}
# B has the title
elseif (in_array($title, $b['titles'])) {
return -1;
}
# No-one has the title
return strcmp($a['lastName'], $b['lastName']);
});
}
问题是数组被一遍又一遍地排序。因此,如果您在 foreach 循环中逐步进行,项目将首先按高级经理职位排序,然后再次按经理职位排序,最后再次按主管职位排序。
所以首先,你必须颠倒你想要的顺序:
$order = array_reverse([
'Senior Manager',
'Manager',
'Supervisor'
]);
那么如果 none 个项目有标题,您就不想四处移动项目。因为在您的其他迭代中,您的 Senior Manager 可能没有 Supervisor 头衔,而其他用户也可能没有该头衔,因此他们都不应更改职位。所以你可以把最后的return改成return 0;
最后,您颠倒了 1
和 -1
的 return,因此您只需将它们调换即可。
最后,最终代码如下所示:
$users = [
[
'lastName' => 'Clarks',
'titles' => ['Manager', 'Supervisor']
],
[
'lastName' => 'Clarkson',
'titles' => ['Sales']
],
[
'lastName' => 'Adams',
'titles' => ['Supervisor']
],
[
'lastName' => 'Adams',
'titles' => ['Manager', 'Senior Manager']
],
[
'lastName' => 'Clarkson',
'titles' => ['Manager']
],
[
'lastName' => 'Davids',
'titles' => ['Senior Manager']
]
];
$order = array_reverse([
'Senior Manager',
'Manager',
'Supervisor'
]);
foreach ($order as $title) {
usort($users, function ($a, $b) use ($title) {
if (in_array($title, $a['titles']) && in_array($title, $b['titles'])) {
return strcmp($a['lastName'], $b['lastName']);
}
# A has the title
elseif (in_array($title, $a['titles'])) {
return -1;
}
# B has the title
elseif (in_array($title, $b['titles'])) {
return 1;
}
return 0;
});
}
输出将是:
array(6) {
[0]=>
array(2) {
["lastName"]=>
string(5) "Adams"
["titles"]=>
array(2) {
[0]=>
string(7) "Manager"
[1]=>
string(14) "Senior Manager"
}
}
[1]=>
array(2) {
["lastName"]=>
string(6) "Davids"
["titles"]=>
array(1) {
[0]=>
string(14) "Senior Manager"
}
}
[2]=>
array(2) {
["lastName"]=>
string(6) "Clarks"
["titles"]=>
array(2) {
[0]=>
string(7) "Manager"
[1]=>
string(10) "Supervisor"
}
}
[3]=>
array(2) {
["lastName"]=>
string(8) "Clarkson"
["titles"]=>
array(1) {
[0]=>
string(7) "Manager"
}
}
[4]=>
array(2) {
["lastName"]=>
string(5) "Adams"
["titles"]=>
array(1) {
[0]=>
string(10) "Supervisor"
}
}
[5]=>
array(2) {
["lastName"]=>
string(8) "Clarkson"
["titles"]=>
array(1) {
[0]=>
string(5) "Sales"
}
}
}
您想要的是根据 $order
中用户头衔的 最低索引 对用户进行排序。您可以使用 array_search
在 $order
中找到他们的每个标题索引,并使用 min
找到最小的数字。如果它们相同,则退回到 strcmp
.
usort($users, function($a, $b) use ($order) {
$minAPos = min(array_map(function($title) use ($order) {
$pos = array_search($title, $order);
return $pos === false? sizeof($order) : $pos;
}, $a['titles']));
$minBPos = min(array_map(function($title) use ($order) {
$pos = array_search($title, $order);
return $pos === false? sizeof($order) : $pos;
}, $b['titles']));
if($minAPos === $minBPos) {
return strcmp($a['lastName'], $b['lastName']);
} else {
return $minAPos <=> $minBPos;
}
});