objects - rsort php
Organizar/distribuir elementos de matriz uniformemente (10)
No estoy seguro de que mi secuencia de comandos con el método sea correcta o no, por favor verifique una vez
<?php
/* multidimensional array */
$data = array(
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "C"),
array( "name" => "SomeName", "type" => "C")
);
/* Assign blank arrays for further use */
$a_array = $b_array = $c_array = $returnArray = array();
$count = count($data); // count array
$x = $y = $z = $m = $a = $b = $c = 0; // assiging variable with value 0
for($i = 0; $i < $count; $i++)
{
if($data[$i][''type''] == "A")
{
$a_array[$x]["name"] = $data[$i]["name"];
$a_array[$x]["type"] = $data[$i]["type"];
$x++ ;
}
elseif($data[$i][''type''] == "B")
{
$b_array[$y]["name"] = $data[$i]["name"];
$b_array[$y]["type"] = $data[$i]["type"];
$y++ ;
}
elseif($data[$i][''type''] == "C")
{
$c_array[$z]["name"] = $data[$i]["name"];
$c_array[$z]["type"] = $data[$i]["type"];
$z++ ;
}
}
for($j = 0; $j < $count; $j++)
{
if($j == 0 || $j % 2 == 0)
{
$returnArray[$m]["name"] = $a_array[$a]["name"];
$returnArray[$m]["type"] = $a_array[$a]["type"];
$a++ ;
}
else
{
if($j == 3 || $j == 7)
{
$returnArray[$m]["name"] = $c_array[$c]["name"];
$returnArray[$m]["type"] = $c_array[$c]["type"];
$c++ ;
}
else
{
$returnArray[$m]["name"] = $b_array[$b]["name"];
$returnArray[$m]["type"] = $b_array[$b]["type"];
$b++ ;
}
}
$m++ ;
}
echo "<pre>";
print_R($returnArray);
?>
Gracias :)
Tengo una matriz asociativa multidimensional con una propiedad de type
. Se parece a esto:
$data = array(
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "C"),
array( "name" => "SomeName", "type" => "C")
);
Quiero reorganizarlo para que los elementos se distribuyan de manera más equitativa (con la menor cantidad de tipos repetitivos si es posible). Debe tener un aspecto como este:
array(
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "C"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "C"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B")
);
Lo que he intentado hasta ahora fue encontrar el recuento para cada tipo y el total:
$count_a = 5;
$count_b = 3;
$count_c = 2;
$total = 10;
Y también la relación de tasa para cada tipo:
$ratio_a = 0.5; //(5/10)
$ratio_b = 0.3; //(3/10)
$ratio_c = 0.2; //(2/10)
Estoy atascado aquí. ¿Debo intentar hacer un nuevo index
propiedad con un número y luego ordenar en función de él? ¿O tal vez utilizar el operador de módulo de alguna manera? También he intentado separar los elementos en 3 matrices diferentes si eso lo hace más fácil.
Algoritmo:
function distribute($data) {
$groups = [];
foreach ($data as $row) {
$groups[$row[''type'']][] = $row;
}
$groupSizes = array_map(''count'', $groups);
asort($groupSizes);
$result = [];
foreach ($groupSizes as $type => $groupSize) {
if (count($result) == 0) {
$result = $groups[$type];
} elseif (count($result) >= count($groups[$type])) {
$result = merge($result, $groups[$type]);
} else {
$result = merge($groups[$type], $result);
}
}
return $result;
}
function merge($a, $b) {
$c1 = count($a);
$c2 = count($b);
$result = [];
$i1 = $i2 = 0;
while ($i1 < $c1) {
$result[] = $a[$i1++];
while ($i2 < $c2 && ($i2+1)/($c2+1) < ($i1+1)/($c1+1)) {
$result[] = $b[$i2++];
}
}
return $result;
}
La idea principal es dividir los datos en grupos y combinar el siguiente grupo más pequeño en el resultado (comenzando con un resultado vacío).
Mientras se fusionan dos matrices, los elementos se ordenan mediante una tecla flotante, que se calcula (en el flujo) en esta línea
while ($i2 < $c2 && ($i2+1)/($c2+1) < ($i1+1)/($c1+1))
como
floatKey = (index + 1) / (groupSize + 1)
(Sin embargo, esta parte puede mejorarse, por lo que la distancia a las "esquinas" ( 0
y 1
) sería la mitad de la distancia entre dos elementos).
En el empate el elemento del grupo más grande es lo primero.
Ejemplo: fusionando AAAA
y BB
las claves para A
serían 0.2, 0.4, 0.6, 0.8
y para B
- 0.33, 0.66
. El resultado sería
A(0.2), B(0.33), A(0.4), A(0.6), B(0.66), A(0.8)
Pruebas:
$testData = [
''AAAAABBBCC'',
''AAAAABBBCCC'',
''ABBCCC'',
''AAAAAABBC'',
''AAAAAABBBBCCD'',
''AAAAAAAAAABC'',
''hpp'',
'''',
''ACCD'', // :-)
];
$results = [];
foreach ($testData as $dataStr) {
$a = str_split($dataStr);
$data = [];
foreach ($a as $type) {
$data[] = [''type'' => $type];
}
$result = distribute($data);
$resultStr = implode(array_column($result, ''type''));
$results[$dataStr] = $resultStr;
}
var_export($results);
Resultados de la prueba:
''AAAAABBBCC'' => ''BACABACABA'',
''AAAAABBBCCC'' => ''CABACABACAB'',
''ABBCCC'' => ''BCACBC'',
''AAAAAABBC'' => ''ABAACAABA'',
''AAAAAABBBBCCD'' => ''BACABADABACAB'',
''AAAAAAAAAABC'' => ''AAACAAAABAAA'',
''hpp'' => ''php'',
'''' => ''sakeofwlrovct'',
''ACCD'' => ''ACDC'',
Demostración de prueba: http://rextester.com/BWBD90255
Puede agregar fácilmente más casos de prueba a la demostración.
Aquí hay otra implementación basada en la idea de la gran respuesta de naktinis :
// split data into arrays of distinct type
$buckets = array_reduce($data, function($result, $item) {
$type = $item["type"];
if (!isset($result[$type])) {
$result[$type] = [];
}
array_push($result[$type], $item);
return $result;
}, []);
// sort buckets by size
usort($buckets, function($a, $b) {
return count($b) - count($a);
});
// merge buckets to single array sorted by type
// and split to chunks of size of the largest bucket
$table = array_chunk(array_merge(...$buckets), count($buckets[0]));
// compute final array by merging each column
$result = [];
foreach (array_keys($table[0]) as $i) {
$result = array_merge($result, array_column($table, $i));
}
Aquí hay una solución que evita repetir patrones siempre que sea posible.
Para AAAAABBBCC
generaría ABABABACAC
;
Para AAAAABBBCCC
generaría ABCABABACAC
;
Además de la clasificación por tipo de recuento, se ejecuta en tiempo lineal (acepta una matriz de datos sin clasificar). El resultado está en $distributed_data
. Para la explicación ver más abajo.
Código
$data = array(
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
);
$distributed_data = array();
$counts = array();
$size = sizeof($data);
// Count values
foreach ($data as $entry) {
$counts[$entry["type"]] = isset($counts[$entry["type"]]) ? $counts[$entry["type"]] + 1 : 1;
}
// Set counter
for ($i = 0; $i < $size; $i++) {
$data[$i]["count"] = $counts[$data[$i]["type"]];
}
// Sort by count
usort($data, function($entry1, $entry2) {
return $entry2["count"] <=> $entry1["count"];
});
// Generate the distributed array
$max_length = $data[0]["count"];
$rows = ceil($size / $max_length);
$last_row = ($size - 1) % $max_length + 1;
$row_cycle = $rows;
$row = 0;
$col = 0;
for ($i = 0; $i < $size; $i++) {
if ($i == $rows * $last_row) {
$row_cycle -= 1;
}
$distributed_data[$i] = $data[$row * $max_length + $col];
$row = ($row + 1) % $row_cycle;
if ($row == 0) {
$col++;
}
}
Explicación
Primero, ordene las entradas de acuerdo con el número de repeticiones que tenga cada tipo. Por ejemplo, CBBCAAB
convierte en BBBAACC
.
Luego imagine una tabla que tenga tantas columnas como la ocurrencia más frecuente (por ejemplo, si tiene AAAABBCC
, la aparición más frecuente sería 4, y la tabla tendría 4 columnas).
Luego escriba todas las entradas en la tabla, de izquierda a derecha, saltando a la nueva fila según sea necesario.
Por ejemplo, para AAAAABBBCCC
obtendrías una tabla como esta:
Para generar la matriz distribuida final, simplemente lea las entradas de arriba hacia abajo, cambiando a una nueva columna según sea necesario.
En el ejemplo anterior, obtendría ABCABABACAC
.
La única forma de obtener entradas repetidas es tener dos de los mismos caracteres en una columna, o encontrar el mismo carácter al cambiar a una columna de la derecha.
El primer escenario no puede suceder porque un grupo de caracteres tendría que ajustarse y esto no puede suceder, porque no hay un grupo de caracteres más largo que el número de columnas (así es como definimos la tabla).
El segundo escenario solo puede ocurrir cuando la segunda fila no está llena. Por ejemplo, AAAABB
deja la segunda fila con dos celdas vacías.
Debe tomar una serie ordenada de tipos ordenados y realizar un recorrido iterativo paso a paso, cambiando el tipo seleccionado en uno.
$data = array(
array( "name" => "SomeName1", "type" => "A"),
array( "name" => "SomeName2", "type" => "A"),
array( "name" => "SomeName3", "type" => "A"),
array( "name" => "SomeName4", "type" => "A"),
array( "name" => "SomeName5", "type" => "A"),
array( "name" => "SomeName6", "type" => "B"),
array( "name" => "SomeName7", "type" => "B"),
array( "name" => "SomeName8", "type" => "B"),
array( "name" => "SomeName9", "type" => "C"),
array( "name" => "SomeName0", "type" => "C")
);
$dataSorted = array();
$counts = array();
foreach($data as $elem) {
// just init values for a new type
if(!isset($counts[$elem[''type'']])) {
$counts[$elem[''type'']] = 0;
$dataByType[$elem[''type'']] = array();
}
// count types
$counts[$elem[''type'']]++;
// save it to grouped array
$dataByType[$elem[''type'']][] = $elem;
}
// sort it to A=>5, B=>3 C=>2
arsort($counts, SORT_NUMERIC);
// get sorted types as an array
$types = array_keys($counts);
// index will be looped 0 -> count($types) - 1 and then down to 0 again
$currentTypeIndex = 0;
// make a walk on sorted array. First get the most popular, then less popular etc.
// when all types are added, repeat
while(count($dataSorted) < count($data)) {
$currentType = $types[$currentTypeIndex];
// skip adding if we ran out this type
if($counts[$currentType]) {
// pop an element of selected type
$dataSorted[] = array_pop($dataByType[$currentType]);
// decrease counter
$counts[$currentType]--;
}
// choose next type
$currentTypeIndex = (++$currentTypeIndex)%count($types);
}
print_r($dataSorted);
El código da salida a elementos en orden de ABCABCABAA.
UPD. la duplicación final tiene lugar en el count(maxtype)
casos count(maxtype)
> count(nexttype) + 1
Marque esta salida exacta de lo que quiere,
$data = array(
array("name" => "SomeName", "type" => "A"),
array("name" => "SomeName1", "type" => "A"),
array("name" => "SomeName2", "type" => "A"),
array("name" => "SomeName3", "type" => "A"),
array("name" => "SomeName4", "type" => "A"),
array("name" => "SomeName5", "type" => "B"),
array("name" => "SomeName6", "type" => "B"),
array("name" => "SomeName7", "type" => "B"),
array("name" => "SomeName8", "type" => "C"),
array("name" => "SomeName9", "type" => "C"),
);
// getting all counts
$type = [];
foreach ($data as $key => $value) {
if (empty($type) || $type != $value[''type'']) {
$type = $value[''type''];
$counter = 0;
}
$temp[$value[''type'']] = ++$counter;
}
/**
* array search with multiple values
*
* @param array $parents input array
* @param array $searched search array
*
* @return int key of found items
*/
function multidimensional_search($parents, $searched)
{
if (empty($searched) || empty($parents)) {
return false;
}
foreach ($parents as $key => $value) {
$exists = true;
foreach ($searched as $skey => $svalue) {
$exists = ($exists && isset($parents[$key][$skey]) && $parents[$key][$skey] == $svalue);
}
if ($exists) {return $key;}
}
return false;
}
$output_array = [];
$first_value = current($temp);
$first_key = key($temp);
$flag = 0;
$junkArr = array_column($data, ''type'', ''name'');
$remember_me = 0;
$incr = 0;
end($temp);
$end_item = key($temp);
reset($temp);
$remember_index = 0;
for ($i = 0; $i < count($data); $i++) {
$output_array[] = $data[multidimensional_search($data, [''name'' => key($junkArr), ''type'' => current($junkArr)])];
if ($temp[$first_key] > 0) {
$temp[$first_key] = --$first_value;
}
$direction = (empty($direction) || $direction == ''reverse'' ? "forward" : "reverse");
for ($k = 0; $k <= $remember_me; $k++) {
if ($direction == ''forward'') {
next($temp);
} else {
prev($temp);
if ($k == 0) {
$incr = $remember_me + 1;
}
}
}
$remember_me = $incr;
if ($remember_me == count($temp) - 1) {
$remember_me = 0;
}
$first_key = key($temp);
$first_value = current($temp);
if (in_array($first_key, $junkArr)) {
$saved_key = key($junkArr);
reset($junkArr);
while ($first_key !== current($junkArr)) {
next($junkArr);
}
unset($junkArr[$saved_key]);
}
}
pr($output_array);
die;
Haz el mapa como quieras.
Pruébalo, funcionará.
Lo que estoy tratando de hacer es,
- buscando todos los contadores de todos los tipos
- entonces lo mapeo con nombre y tipo para que podamos identificarnos de forma única por nombre
- entonces estoy usando la variable de puntero en la temperatura para rastrear el número de cosas que quedan para cada tipo
- Estoy rellenando la matriz de salida con un par de valor de clave único para cada tipo de arriba a abajo.
- Usé junkarray para mover el puntero con la ayuda del conteo restante.
Puedes usar así
<?php
$data = array(
array( "name" => "SomeName 1", "type" => "A"),
array( "name" => "SomeName 2", "type" => "A"),
array( "name" => "SomeName 3", "type" => "A"),
array( "name" => "SomeName 4", "type" => "A"),
array( "name" => "SomeName 5", "type" => "A"),
array( "name" => "SomeName 6", "type" => "B"),
array( "name" => "SomeName 7", "type" => "B"),
array( "name" => "SomeName 8", "type" => "B"),
array( "name" => "SomeName 9", "type" => "C"),
array( "name" => "SomeName 10", "type" => "C")
);
$result = array();
$typeArr = array();
$countArr = array();
$ratioArr = array();
foreach($data as $t){
$typeArr[$t[''type'']][] = $t;
$countArr[$t[''type'']] = count($typeArr[$t[''type'']]);
$ratioArr[$t[''type'']] = $countArr[$t[''type'']]/ count($data);
}
arsort($countArr);
$countArrIndex = array_keys($countArr);
$maxKeyCount = 0 ;$exceptMaxKey = 1;
$exceptMaxKeyCount=0;
for($i = 0; $i<count($data); $i++){
if($i%2 != 0 ){
$result[$i] = $typeArr[$countArrIndex[$exceptMaxKey]][$exceptMaxKeyCount];
if($exceptMaxKey == (count($typeArr)-1)){
$exceptMaxKey = 1;
$exceptMaxKeyCount++;
}else{
$exceptMaxKey++;
}
}else{
$result[$i] = $typeArr[$countArrIndex[0]][$maxKeyCount];
$maxKeyCount ++;
}
}
echo "<pre>";
print_r($result);
$countArr[''total''] = count($data);
print_r($countArr);
print_r($ratioArr);
Gracias,
Si todo lo que desea es reordenar la lista para minimizar la repetición, una vez que tenga los conteos de cada tipo, como
$count_a = 5;
$count_b = 3;
$count_c = 2;
$total = 10;
luego puede seleccionar el tipo más poblado, seleccionar una línea de ese tipo y colocarlo en la posición 0 de la lista que está creando. Luego disminuya el conteo para ese tipo y seleccione el tipo más populoso que no se seleccionó solo. Continúe hasta que solo quede un tipo y colóquelos en el final de la lista.
Dios mío, tantas funciones enormes aquí.
solicitado ABAC ABAC ... hecho:
function sortTypes(array $data, array $types)
{
$result = [];
while (!empty($data)) {
$currentType = current($types);
if (!next($types)) {
reset($types);
}
foreach ($data as $key => $array) {
if ($array[''type''] === $currentType) {
$result[$key] = $array;
unset($data[$key]);
break;
}
}
}
return $result;
}
$types = [''A'', ''B'', ''A'', ''C'']; // gets sorted by this pattern
$result = sortTypes($data, $types);
Prueba:
var_export($result);
// OUT:
[
0 => [
''name'' => ''SomeName'',
''type'' => ''A'',
],
5 => [
''name'' => ''SomeName'',
''type'' => ''B'',
],
1 => [
''name'' => ''SomeName'',
''type'' => ''A'',
],
8 => [
''name'' => ''SomeName'',
''type'' => ''C'',
],
2 => [
''name'' => ''SomeName'',
''type'' => ''A'',
],
6 => [
''name'' => ''SomeName'',
''type'' => ''B'',
],
3 => [
''name'' => ''SomeName'',
''type'' => ''A'',
],
9 => [
''name'' => ''SomeName'',
''type'' => ''C'',
],
4 => [
''name'' => ''SomeName'',
''type'' => ''A'',
],
7 => [
''name'' => ''SomeName'',
''type'' => ''B'',
],
]
$data = array(
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "A"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "B"),
array( "name" => "SomeName", "type" => "C"),
array( "name" => "SomeName", "type" => "C")
);
//make seperate arrays
echo "<pre>";
foreach($data as $val){
${$val["type"]}[]=$val["name"];
$types[]=$val[''type''];
}
$types=array_unique($types);
//make ratio
foreach($types as $val){
$cnt[]=count($$val);
}
//find maximum from ratio
echo $max=max($cnt);
echo $min=min($cnt);
for($i=0;$i<$max;$i++){
foreach($types as $val){
if(isset($$val[$i])){
$new_array[]=array("name"=>$$val[$i],"type"=>$val);
}
}
}
print_r($new_array);
Fiddle: http://phpfiddle.org/main/code/ju2k-abte
Explicación
- Step 1: Make separate array - Step 2: Count all array and find out the ratio - Step 3: Iterate with array with maximum ratio value - Step 4: Make array with same index together and merge them in multidimensional array