如果它们具有相同的值,则在关联数组中对键的顺序进行随机播放?

时间:2016-03-07 14:53:38

标签: php arrays sorting random associative-array

给定一个像这样的关联数组,你如何改变具有相同值的键的顺序?

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

2 个答案:

答案 0 :(得分:5)

我重写了整个代码,因为我发现了另一种比旧代码更简单,更快的方法(如果你仍然对旧版see the revision感兴趣):

  • 旧代码(100,000次执行):Ø4.4秒。
  • 新代码(100,000次执行):Ø1.3秒

解释

首先,我们使用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]);
    

  • 如果需要,您还可以进行一些优化更改:

    1. 如果你得到一个空数组,则抢先返回,例如

      function randomize_duplicate_array_value_keys(array $arr){
       
          if(empty($arr))
              return [];
       
          $uniqueValues = array_flip($arr);
          $result = [];
          //***
      }
    2. 如果数组没有重复值,则抢先返回数组:

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