У меня есть массив, который можно разделить на 3 разные группы по одному из его атрибутов:
$shuffleMeGood = array(
0 => array('id' => '1', 'group' => 'banana'),
1 => array('id' => '2', 'group' => 'banana'),
2 => array('id' => '3', 'group' => 'banana'),
3 => array('id' => '4', 'group' => 'airplane'),
4 => array('id' => '5', 'group' => 'coconut'),
5 => array('id' => '6', 'group' => 'coconut')
...
);
Размер групп может варьироваться, может быть 7 элементов кокоса и 5 элементов самолета и так далее. Как я могу перетасовать массив таким образом, чтобы не было или было наименьшее возможное вхождение соседей одной и той же группы?
Я попытался разделить их на 3 разных массива и снова объединить попарно в соотношении, основанном на их размере. Но с группами, у которых есть отношение близко, 5 я заканчиваю тем, что имел блок одной группы в конце.
РЕДАКТИРОВАТЬ: Адаптировал ответ в соответствии с комментариями ОП о том, как он хочет обрабатывать оставшиеся предметы. Сохраняя мой оригинальный ответ ниже.
Мы будем равномерно распределять наибольшую группу друг в друге, вычисляя, сколько мы можем поместить в каждую группу, и используя несколько промежуточных массивов:
<?php
// Your array changed to PHP with some added items
$a = array(
array(
'id'=>1,
'group'=>'banana'
),
array(
'id'=>2,
'group'=>'banana'
),
array(
'id'=>3,
'group'=>'banana'
),
array(
'id'=>4,
'group'=>'airplane'
),
array(
'id'=>5,
'group'=>'coconut'
),
array(
'id'=>6,
'group'=>'coconut'
),
array(
'id'=>7,
'group'=>'coconut'
),
array(
'id'=>8,
'group'=>'coconut'
),
array(
'id'=>9,
'group'=>'coconut'
),
array(
'id'=>10,
'group'=>'coconut'
),
);
// We want to know each of group there are
// We also want to arrange the items in a new "perGroup" array.
$counts = array();
$perGroup = array();
foreach($a as $key=>$value){
// Avoid PHP undefined notices
if(isset($counts[$value['group']])){
$counts[$value['group']]++;
}else{
$counts[$value['group']] = 1;
}
// Avoid PHP undefined notices
if(isset($perGroup[$value['group']])){
$perGroup[$value['group']][] = $value;
}else{
$perGroup[$value['group']] = array();
$perGroup[$value['group']][] = $value;
}
}
// Sort the count from largest to lowest
arsort($counts);
// Get all the group names
$keys = array_keys($counts);
// We'll have as many unique groups as the SECOND largest item.
$nbOfUniques = $counts[$keys[1]];
// Calculate how many of the largest we need to put in each unique
$nbOfLargestPerUnique = ceil($counts[$keys[0]] / $nbOfUniques);
// Get the largest group name for easier use
$largestGroupName = $keys[0];
$finalArray = array();
// Loop for how many unique groups we have
for ($i = 1; $i <= $nbOfUniques; $i++) {
// For each group...
foreach($keys as $k=>$v){
// If its the largest
if($v == $largestGroupName){
// Add as many as needed
for ($j = 1; $j <= $nbOfLargestPerUnique; $j++) {
// Make sure we actually have an item, as the number is not always exact.
// Using array_shift() to reduce our array
if(($item = array_shift($perGroup[$v])) !== NULL){
$finalArray[] = $item;
}
}
}else{
// Not the largest group, just add one
if(($item = array_shift($perGroup[$v])) !== NULL){
$finalArray[] = $item;
}
}
}
}
echo '<pre>';
var_dump($finalArray);
echo '</pre>';
?>
ОРИГИНАЛ:
Я бы использовал рекурсивную функцию с двумя массивами: последний и копия исходного, который становится все меньше и меньше, пока мы не обработаем все это:
<?php
// Your array changed to PHP with some added items
$a = array(
array(
'id'=>1,
'group'=>'banana'
),
array(
'id'=>2,
'group'=>'banana'
),
array(
'id'=>3,
'group'=>'banana'
),
array(
'id'=>4,
'group'=>'airplane'
),
array(
'id'=>5,
'group'=>'coconut'
),
array(
'id'=>6,
'group'=>'coconut'
),
array(
'id'=>7,
'group'=>'coconut'
),
array(
'id'=>8,
'group'=>'coconut'
),
array(
'id'=>9,
'group'=>'coconut'
),
array(
'id'=>10,
'group'=>'coconut'
),
);
// Our recursive function
// $array is the array to be ordered (in our case, $a)
// $orderedArray is needed for the recusivity part
function orderArray($array, $orderedArray = array()){
// Remember the last processed group
$lastGroup = '';
// Loop on all $array's items
foreach($array as $key=>$value){
// If NOT the same as the last group
if($value['group'] != $lastGroup){
// Add it to our orderedArray
$orderedArray[] = $value;
// Remember its group
$lastGroup = $value['group'];
// Remove it from the original array, as it has already been processed
unset($array[$key]);
}
}
// There are still items to be processed
if(count($array) > 0){
// Call ourselves again, with the same arrays
return orderArray($array, $orderedArray);
}else{
// Done processing, return the $orderedArray
return $orderedArray;
}
}
echo '<pre>';
var_dump(orderArray($a));
echo '</pre>';
?>
Однако, если одна группа явно превосходит другие по численности (например, кокос в моем примере), в конце вы получите кучу кокосов.
Надеюсь, это помогло.
Других решений пока нет …