我有一个数组数组:
Array (
[0] => Array (
[id] = 7867867,
[title] = 'Some Title'),
[1] => Array (
[id] = 3452342,
[title] = 'Some Title'),
[2] => Array (
[id] = 1231233,
[title] = 'Some Title'),
[3] => Array (
[id] = 5867867,
[title] = 'Some Title')
)
需要按特定顺序进行:
我该怎么做呢?我之前已对数组进行了排序,并阅读了大量关于它的帖子,但它们总是基于比较(即valueA< valueB)。
非常感谢帮助。
答案 0 :(得分:99)
您可以使用usort()
精确指定数组的排序方式。在这种情况下,$order
数组可以在比较函数中使用。
以下示例使用closure
来简化生活。
$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
array('id' => 7867867, 'title' => 'Some Title'),
array('id' => 3452342, 'title' => 'Some Title'),
array('id' => 1231233, 'title' => 'Some Title'),
array('id' => 5867867, 'title' => 'Some Title'),
);
usort($array, function ($a, $b) use ($order) {
$pos_a = array_search($a['id'], $order);
$pos_b = array_search($b['id'], $order);
return $pos_a - $pos_b;
});
var_dump($array);
这项工作的关键是让正在比较的值成为id
数组中$order
的位置。
比较函数通过在$order
数组中查找要比较的两个项的id的位置来工作。如果$a['id']
位于$b['id']
数组中的$order
之前,则函数的返回值将为负数($a
较少,因此“浮动”到顶部)。如果$a['id']
位于$b['id']
之后,则函数返回正数($a
更大,因此“下沉”)。
最后,使用封闭没有特殊原因;这只是我快速编写这些一次性功能的首选方式。它同样可以使用普通的命名函数。
答案 1 :(得分:14)
为此额外要求扩展salathe's answer:
现在当我向数组添加项目而不是排序时会发生什么?一世 不管他们出现什么样的顺序,只要它出现在那些顺序之后 我确实指明了。
您需要在排序功能中添加两个附加条件:
所以修改后的代码是:
$order = array(
3452342,
5867867,
7867867,
1231233
);
$array = array(
array("id" => 7867867, "title" => "Must Be #3"),
array("id" => 3452342, "title" => "Must Be #1"),
array("id" => 1231233, "title" => "Must Be #4"),
array("id" => 5867867, "title" => "Must Be #2"),
array("id" => 1111111, "title" => "Dont Care #1"),
array("id" => 2222222, "title" => "Dont Care #2"),
array("id" => 3333333, "title" => "Dont Care #3"),
array("id" => 4444444, "title" => "Dont Care #4")
);
function custom_compare($a, $b){
global $order;
$a = array_search($a["id"], $order);
$b = array_search($b["id"], $order);
if($a === false && $b === false) { // both items are dont cares
return 0; // a == b
}
else if ($a === false) { // $a is a dont care item
return 1; // $a > $b
}
else if ($b === false) { // $b is a dont care item
return -1; // $a < $b
}
else {
return $a - $b;
}
}
shuffle($array); // for testing
var_dump($array); // before
usort($array, "custom_compare");
var_dump($array); // after
输出:
Before | After
-------------------------------+-------------------------------
array(8) { | array(8) {
[0]=> | [0]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(4444444) | int(3452342)
["title"]=> | ["title"]=>
string(12) "Dont Care #4" | string(10) "Must Be #1"
} | }
[1]=> | [1]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(3333333) | int(5867867)
["title"]=> | ["title"]=>
string(12) "Dont Care #3" | string(10) "Must Be #2"
} | }
[2]=> | [2]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(1231233) | int(7867867)
["title"]=> | ["title"]=>
string(10) "Must Be #4" | string(10) "Must Be #3"
} | }
[3]=> | [3]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(1111111) | int(1231233)
["title"]=> | ["title"]=>
string(12) "Dont Care #1" | string(10) "Must Be #4"
} | }
[4]=> | [4]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(5867867) | int(2222222)
["title"]=> | ["title"]=>
string(10) "Must Be #2" | string(12) "Dont Care #2"
} | }
[5]=> | [5]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(2222222) | int(1111111)
["title"]=> | ["title"]=>
string(12) "Dont Care #2" | string(12) "Dont Care #1"
} | }
[6]=> | [6]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(3452342) | int(3333333)
["title"]=> | ["title"]=>
string(10) "Must Be #1" | string(12) "Dont Care #3"
} | }
[7]=> | [7]=>
array(2) { | array(2) {
["id"]=> | ["id"]=>
int(7867867) | int(4444444)
["title"]=> | ["title"]=>
string(10) "Must Be #3" | string(12) "Dont Care #4"
} | }
} | }
答案 2 :(得分:4)
答案 3 :(得分:4)
使用array_search()
迭代调用方法的其他答案效率不高。通过重组/翻转“订单”查找数组,您可以完全省略所有array_search()
次调用 - 使您的任务更加高效和简洁。我将使用最现代的“宇宙飞船运营商”(<=>
),但早期的技术对比较线的作用相同。
方法#1 - usort
当id
(Demo)
$order
值时
$order=array_flip([3452342,5867867,7867867,1231233]); // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$array=[
['id'=>7867867,'title'=>'Some Title'],
['id'=>3452342,'title'=>'Some Title'],
['id'=>1231233,'title'=>'Some Title'],
['id'=>5867867,'title'=>'Some Title']
];
usort($array,function($a,$b)use($order){
return $order[$a['id']]<=>$order[$b['id']];
// when comparing ids 3452342 & 1231233, the actual comparison is 0 vs 3
});
// uasort() if you want to preserve keys
var_export($array);
方法#2 - usort
当id
(Demo)中不存在某些$order
值时
*注意,isset()
是一个比array_search()
$order=array_flip([3452342,5867867,7867867,1231233]); // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$outlier=1+max($order);
// generating $outlier=4
$array=[
['id'=>7867867,'title'=>'Some Title'],
['id'=>3452342,'title'=>'Some Title'],
['id'=>'foo','title'=>'Some Title'],
['id'=>1231233,'title'=>'Some Title'],
['id'=>'bar','title'=>'Some Title'],
['id'=>5867867,'title'=>'Some Title']
];
usort($array,function($a,$b)use(&$order,$outlier){ // make $order modifiable with &
if(!isset($order[$a['id']])){$order[$a['id']]=$outlier;} // update lookup array with [id]=>[outlier number]
if(!isset($order[$b['id']])){$order[$b['id']]=$outlier;} // and again
return $order[$a['id']]<=>$order[$b['id']];
});
var_export($array);
替代方法#2 - usort
id
$order
个值时
...我还想提一下,在某些情况下,避免isset()
的迭代双重调用可能不如在调用{{1}之前完全准备$order
数组}}
这个单行将确保没有丢失usort()
值,因此除了排序函数内的比较行之外不需要任何其他内容。 (Full Snippet Demo)
id
答案 4 :(得分:1)
@salathe对于那些很难理解salathe's usort正在做什么的人:
$ array中的每个项目都是&#39;冠军&#39;在锦标赛中,在一个新阵列的开头(除了不是第一个,他们想成为0号)。
$ a是主场冠军,$ b是对手中的对手冠军。
来自回调的$ pos_a和$ pos_b是在争夺冠军a和b时使用的属性。在这种情况下,此属性是$ order中的冠军id的索引。然后就会有回归的斗争。现在我们看看是否有更多或更少的属性更好。在一场激烈的战斗中,主场冠军想要一个负数,这样他就可以更快地进入阵中。客场冠军想要一个正数。并且应该有一个0是一个平局。
所以按照这个比喻,当离开冠军属性($ order中的索引)从主队属性中减去时,客场冠军属性越大,获得正数就越不可能获胜。但是,如果您要颠倒使用属性的方式,那么现在可以从客场冠军中减去主场冠军的属性。在这种情况下,客场冠军的更多数字更有可能使他的比赛结束为正数。
代码如下:
注意:代码运行很多次就像真正的锦标赛有很多战斗,以决定谁先获得(即0 /阵列开始)
//tournament with goal to be first in array
usort($champions, function ($home, $away) use ($order) {
$home_attribute = array_search($a['id'], $order);
$away_attribute = array_search($b['id'], $order);
//fight with desired outcome for home being negative and away desiring positive
return $home_attribute - $away_attribute;
});
答案 5 :(得分:0)
如果没有排序,您也可以得到它。
没有重复的ID;
<?php
$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
array('id' => 7867867, 'title' => 'Some Title'),
array('id' => 3452342, 'title' => 'Some Title'),
array('id' => 1231233, 'title' => 'Some Title'),
array('id' => 5867867, 'title' => 'Some Title'),
);
$order = array_flip($order);
$array = array_column($array,null,"id");
$result = array_replace($order,$array);
var_dump(array_values($result));
具有重复的ID,
<?php
$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
array('id' => 7867867, 'title' => 'Some Title'),
array('id' => 3452342, 'title' => 'Some Title'),
array('id' => 1231233, 'title' => 'Some Title'),
array('id' => 5867867, 'title' => 'Some Title'),
);
$order_dict = array_flip($order);
$order_dict = array_combine($order,array_fill(0,count($order),[]));
foreach($array as $item){
$order_dict[$item["id"]][] = $item;
}
//$order_dict = array_filter($order_dict); // if there is empty item on some id in $order array
$result = [];
foreach($order_dict as $items){
foreach($items as $item){
$result[] = $item;
}
}
var_dump($result);
答案 6 :(得分:0)
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict) { return $dict[$elem['id']] ?? INF; }, $array);
array_multisort($positions, $array);
当数组很大或获取ID的成本较高时,使用usort()
可能会很糟糕,因为您需要为每个比较重新计算ID。尝试将array_multisort()
与预先计算的位置一起使用(请参见下面的示例中的mediumsort
或fastsort
),这并不复杂。
在每个比较中都在order数组中搜索id(就像在接受的答案中一样)不会提高性能,因为您会在每个比较中对其进行迭代。计算一次。
在下面的代码段中,您可以看到主要的三个排序功能:
slowsort
mediumsort
slowsort
fastsort
mediumsort
。请注意,这些ID的后备元素通过提供后备值INF
来处理ID顺序不正确的元素。如果您的订单数组与原始数组的ID一对一匹配,则避免全部排序,而只是将元素插入正确的位置即可。我添加了一个功能cheatsort
,正是该功能。
您通常可以按权重对数组进行排序(请参见示例中的weightedsort
)。确保只计算一次重量,以获得良好的性能。
fastsort about 1 ms
mediumsort about 3 ms
slowsort about 60 ms
提示:对于更大的数组,差异会变得更糟。
<?php
/**
* accepted answer
*
* re-evaluate position in order on each comparison
*/
function slowsort(&$array, $order, $key = 'id')
{
usort($array, function ($a, $b) use ($order, $key) {
$pos_a = array_search($a[$key], $order);
$pos_b = array_search($b[$key], $order);
return $pos_a - $pos_b;
});
}
/**
* calculate element positions once
*/
function mediumsort(&$array, $order, $key = 'id')
{
$positions = array_map(function ($elem) use ($order, $key) {
return array_search($elem[$key], $order);
}, $array);
array_multisort($positions, $array);
}
/**
* calculate positions without searching
*/
function fastsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict, $key) {
return $dict[$elem[$key]] ?? INF;
}, $array);
array_multisort($positions, $array);
}
/**
* when each order element gets used exactly once, insert elements directly
*/
function cheatsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$copy = $array;
foreach ($copy as $elem) {
$pos = $dict[$elem[$key]];
$array[$pos] = $elem;
}
}
/**
* Sort elements in $array by their weight given by $weight_func
*
* You could rewrite fastsort and mediumsort by replacing $position by a weight function
*/
function weightedsort(&$array, $weight_func)
{
$weights = array_map($weight_func, $array);
array_multisort($weights, $array);
}
/**
* MEASUREMENTS
*/
/**
* Generate the sorting problem
*/
function generate($size = 1000)
{
$order = array();
$array = array();
for ($i = 0; $i < $size; $i++) {
$id = random_int(0, PHP_INT_MAX);
$order[] = $id;
$array[] = array('id' => $id);
}
shuffle($order);
return [$array, $order];
}
/**
* Time $callable in ms
*/
function time_it($callable)
{
$then = microtime(true);
$callable();
$now = microtime(true);
return 1000 * ($now - $then);
}
/**
* Time a sort function with name $sort_func
*/
function time_sort($sort_func)
{
echo "Timing $sort_func", PHP_EOL;
[$array, $order] = generate();
echo time_it(function () use ($sort_func, &$array, $order) {
$sort_func($array, $order);
}) . ' ms' . PHP_EOL;
}
time_sort('cheatsort');
time_sort('fastsort');
time_sort('mediumsort');
time_sort('slowsort');
答案 7 :(得分:-1)
这是我根据id
值以ASC顺序对多维数组进行排序的方法:
$arrayFilter = array(
array('product_tag' => 'xenia', 'id' => 4),
array('product_tag' => 'worn', 'id' => 5),
array('product_tag' => 'woven', 'id' => 3),
array('product_tag' => 'nude', 'id' => 1)
);
for ($i = 0; $i < sizeof($arrayFilter); $i++) {
for ($j=$i+1; $j < sizeof($arrayFilter); $j++) {
if ($arrayFilter[$i]['id'] > $arrayFilter[$j]['id']) {
$c = $arrayFilter[$i];
$arrayFilter[$i] = $arrayFilter[$j];
$arrayFilter[$j] = $c;
}
}
}
print_r($arrayFilter);
<强>输出:强>
Array
(
[0] => Array
(
[product_tag] => nude
[id] => 1
)
[1] => Array
(
[product_tag] => woven
[id] => 3
)
[2] => Array
(
[product_tag] => xenia
[id] => 4
)
[3] => Array
(
[product_tag] => worn
[id] => 5
)
)