给定一个像这样的关联数组,你如何改变具有相同值的键的顺序?
array(a => 1,
b => 2, // make b or c ordered first, randomly
c => 2,
d => 4,
e => 5, // make e or f ordered first, randomly
f => 5);
我尝试的方法是将其转换为这样的结构并将值(即原始键的数组)混洗,然后将其展平为原始形式。有更简单或更清洁的方法吗? (我不担心效率,这适用于小型数据集。)
array(1 => [a],
2 => [b, c], // shuffle these
4 => [d],
5 => [e, f]); // shuffle these
function array_sort_randomize_equal_values($array) {
$collect_by_value = array();
foreach ($array as $key => $value) {
if (! array_key_exists($value, $collect_by_value)) {
$collect_by_value[$value] = array();
}
// note the &, we want to modify the array, not get a copy
$subarray = &$collect_by_value[$value];
array_push($subarray, $key);
}
arsort($collect_by_value);
$reordered = array();
foreach ($collect_by_value as $value => $array_of_keys) {
// after randomizing keys with the same value, create a new array
shuffle($array_of_keys);
foreach ($array_of_keys as $key) {
array_push($reordered, $value);
}
}
return $reordered;
}
答案 0 :(得分:5)
我重写了整个代码,因为我发现了另一种比旧代码更简单,更快的方法(如果你仍然对旧版see the revision感兴趣):
首先,我们使用array_flip()
从数组中获取所有唯一值,因为这些值是键,并且您不能在数组中拥有重复键,我们拥有唯一值。我们还创建了一个数组$result
,然后将结果存储在其中,$keyPool
用于存储每个值的所有键。
现在我们遍历我们的唯一值,并将具有相同值的所有键放入具有array_keys()
的数组中,并将其保存在$keyPool
中,并将值作为键。我们也可以立即shuffle()
密钥数组,以便它们已经是随机的:
foreach($uniqueValues as $value => $notNeeded){
$keyPool[$value] = array_keys($arr, $value, TRUE);
shuffle($keyPool[$value]);
}
现在,我们已经可以遍历原始数组并从$keyPool
获取每个值array_shift()
的密钥,并将其保存在$result
中:
foreach($arr as $value)
$result[array_shift($keyPool[$value])] = $value;
由于我们已经对数组进行了混乱,因此键已经有一个随机顺序,我们只使用array_shift()
,因此我们无法使用该键两次。
<?php
$arr = ["a" => 1, "b" => 1, "c" => 1, "d" => 1, "e" => 1, "f" => 2,
"g" => 1, "h" => 3, "i" => 4, "j" => 5, "k" => 5];
function randomize_duplicate_array_value_keys(array $arr){
$uniqueValues = array_flip($arr);
$result = [];
$keyPool = [];
foreach($uniqueValues as $value => $notNeeded){
$keyPool[$value] = array_keys($arr, $value, TRUE);
shuffle($keyPool[$value]);
}
foreach($arr as $value)
$result[array_shift($keyPool[$value])] = $value;
return $result;
}
$result = randomize_duplicate_array_value_keys($arr);
print_r($result);
?>
(可能)输出:
Array (
[b] => 1
[g] => 1
[a] => 1
[e] => 1
[d] => 1
[f] => 2
[c] => 1
[h] => 3
[i] => 4
[k] => 5
[j] => 5
)
我使用array_flip()
代替array_unique()
来获取数组中的唯一值,因为它稍快一些。
我还删除了if语句来检查数组是否有多个元素并且需要进行混洗,因为有和没有if语句,代码运行的时间与执行时间相同。我刚删除它以使其更容易理解,代码更易读:
if(count($keyPool[$value]) > 1)
shuffle($keyPool[$value]);
如果需要,您还可以进行一些优化更改:
如果你得到一个空数组,则抢先返回,例如
function randomize_duplicate_array_value_keys(array $arr){
if(empty($arr))
return [];
$uniqueValues = array_flip($arr);
$result = [];
//***
}
如果数组没有重复值,则抢先返回数组:
function randomize_duplicate_array_value_keys(array $arr){
if(empty($arr))
return [];
elseif(empty(array_filter(array_count_values($arr), function($v){return $v > 1;})))
return [];
$uniqueValues = array_flip($arr);
$result = [];
//***
}
答案 1 :(得分:1)
这是另一种遍历排序数组同时跟踪前一个值的方法。如果先前的值与当前值不同,则先前的值将添加到新数组,而当前值将变为先前的值。如果当前值与前一个值相同,那么根据rand(0,1)
的结果,先前的值会像以前一样添加到新列表中,或者当前值首先添加到新列表中:
<?php
$l = ['a' => 1,'b' => 2, 'c' => 2,
'd' => 4,'e' => 5,'f' => 5];
asort($l);
$prevK = key($l);
$prevV = array_shift($l); //initialize prev to 1st element
$shuffled = [];
foreach($l as $k => $v) {
if($v != $prevV || rand(0,1)) {
$shuffled[$prevK] = $prevV;
$prevK = $k;
$prevV = $v;
}
else {
$shuffled[$k] = $v;
}
}
$shuffled[$prevK] = $prevV;
print_r($shuffled);