anson kao
Tengo una matriz de subarreglos en el siguiente formato:
[
'a' => ['id' => 20, 'name' => 'chimpanzee'],
'b' => ['id' => 40, 'name' => 'meeting'],
'c' => ['id' => 20, 'name' => 'dynasty'],
'd' => ['id' => 50, 'name' => 'chocolate'],
'e' => ['id' => 10, 'name' => 'bananas'],
'f' => ['id' => 50, 'name' => 'fantasy'],
'g' => ['id' => 50, 'name' => 'football']
]
Y me gustaría agruparlo en una nueva matriz según el campo de identificación en cada subarreglo.
array
(
10 => array
(
e => array ( id = 10, name = bananas )
)
20 => array
(
a => array ( id = 20, name = chimpanzee )
c => array ( id = 20, name = dynasty )
)
40 => array
(
b => array ( id = 40, name = meeting )
)
50 => array
(
d => array ( id = 50, name = chocolate )
f => array ( id = 50, name = fantasy )
g => array ( id = 50, name = football )
)
)
tim cooper
$arr = array();
foreach ($old_arr as $key => $item) {
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
-
@Herbert, ¿supongo que escribir en una identificación inexistente afecta el rendimiento? o dispara advertencias de PHP?
–Anson Kao
27/09/2011 a las 20:10
-
@SampleJACK: Mi error. A primera vista, pensé que estaba verificando que existe una identificación en
$old_arr
. Ahora que lo examino más de cerca, usandoarray_key_exists
no agrega nada a este código. El resultado es exactamente el mismo sin él. En términos de rendimiento: llama a una función en una matriz dentro de un bucle que tiene que compensar cualquier impacto de rendimiento que reciba al escribir en una clave inexistente, por lo que sugiero dejar todoif()
bloquear.– Herbert
27/09/2011 a las 20:30
-
@Herbert: Lo agregué porque pensé que se mostraría un error si el umbral de informe de errores era demasiado bajo. Lo probé y no parece quejarse.
–Tim Cooper
27/09/2011 a las 20:39
-
@Tim: Sí, tengo mi informe de errores activado para mostrar todo y, tienes razón, no tengo quejas. No quise insinuar que era un código incorrecto de ninguna manera. SampleJACK mencionó el rendimiento y, después de pensarlo, tiene sentido dejarlo. Con toda honestidad, pensé que estaba verificando las identificaciones en las matrices internas. Eso me enseñará a leer con más atención. :p Todavía recibes mi +1 por un buen código.
– Herbert
27/09/2011 a las 20:50
-
Agregué una respuesta para la posteridad para aclarar de lo que he estado hablando.
– Herbert
27/09/2011 a las 20:51
foreach($array as $key => $value){
$newarray[$value['id']][$key] = $value;
}
var_dump($newarray);
pan comido 😉
-
Probablemente igualmente fácil de explicar cómo funciona su código y por qué cree que es la mejor técnica para usar.
– mickmackusa
17 de junio de 2020 a las 8:14
-
Pero realmente, no hay ningún valor nuevo en mantener esta respuesta en la página. Esta respuesta de solo código (un duplicado exacto de la técnica de Tim) se publicó 10 minutos después de que Tim publicara.
– mickmackusa
17 de junio de 2020 a las 23:25
El siguiente código adapta el código de @Tim Cooper para mitigar Undefined index: id
errores en el caso de que una de las matrices internas no contenga un identificación:
$arr = array();
foreach($old_arr as $key => $item)
{
if(array_key_exists('id', $item))
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
Sin embargo, eliminará las matrices internas sin una identificación.
P.ej
$old_arr = array(
'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
'b' => array ( 'id' => 40, 'name' => 'meeting' ),
'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
'e' => array ( 'id' => 10, 'name' => 'bananas' ),
'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
'g' => array ( 'id' => 50, 'name' => 'football' ),
'h' => array ( 'name' => 'bob' )
);
dejará caer la matriz ‘h’ por completo.
-
Este es un “problema inventado”, no representado en la pregunta del OP. Probablemente sea mejor encontrar otra pregunta que plantee este problema y publicarla allí.
– mickmackusa
17 de junio de 2020 a las 23:13
Piotr Olaszewski
También puedes usar Arrays::groupBy() de golosinas de ouzo:
$groupBy = Arrays::groupBy($array, Functions::extract()->id);
print_r($groupBy);
Y resultado:
Array
(
[20] => Array
(
[0] => Array
(
[id] => 20
[name] => chimpanzee
)
[1] => Array
(
[id] => 20
[name] => dynasty
)
)
[40] => Array
(
[0] => Array
(
[id] => 40
[name] => meeting
)
)
[50] => Array
(
[0] => Array
(
[id] => 50
[name] => chocolate
)
[1] => Array
(
[id] => 50
[name] => fantasy
)
[2] => Array
(
[id] => 50
[name] => football
)
)
[10] => Array
(
[0] => Array
(
[id] => 10
[name] => bananas
)
)
)
Aquí hay una función que tomará una matriz como primer argumento y un criterio (una cadena o función de devolución de llamada) como segundo argumento. La función devuelve una nueva matriz que agrupa la matriz como se solicitó.
/**
* Group items from an array together by some criteria or value.
*
* @param $arr array The array to group items from
* @param $criteria string|callable The key to group by or a function the returns a key to group by.
* @return array
*
*/
function groupBy($arr, $criteria): array
{
return array_reduce($arr, function($accumulator, $item) use ($criteria) {
$key = (is_callable($criteria)) ? $criteria($item) : $item[$criteria];
if (!array_key_exists($key, $accumulator)) {
$accumulator[$key] = [];
}
array_push($accumulator[$key], $item);
return $accumulator;
}, []);
}
Aquí está la matriz dada:
$arr = array(
'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
'b' => array ( 'id' => 40, 'name' => 'meeting' ),
'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
'e' => array ( 'id' => 10, 'name' => 'bananas' ),
'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
'g' => array ( 'id' => 50, 'name' => 'football' )
);
Y ejemplos usando la función con una cadena y una función de devolución de llamada:
$q = groupBy($arr, 'id');
print_r($q);
$r = groupBy($arr, function($item) {
return $item['id'];
});
print_r($r);
Los resultados son los mismos en ambos ejemplos:
Array
(
[20] => Array
(
[0] => Array
(
[id] => 20
[name] => chimpanzee
)
[1] => Array
(
[id] => 20
[name] => dynasty
)
)
[40] => Array
(
[0] => Array
(
[id] => 40
[name] => meeting
)
)
[50] => Array
(
[0] => Array
(
[id] => 50
[name] => chocolate
)
[1] => Array
(
[id] => 50
[name] => fantasy
)
[2] => Array
(
[id] => 50
[name] => football
)
)
[10] => Array
(
[0] => Array
(
[id] => 10
[name] => bananas
)
)
)
Pasar la devolución de llamada es una exageración en el ejemplo anterior, pero usar la devolución de llamada encuentra su uso cuando pasa una matriz de objetos, una matriz multidimensional o tiene algo arbitrario por el que desea agrupar.
SaeedPooyanfar
Tal vez valga la pena mencionar que también puedes usar php array_reduce
función
$items = [
['id' => 20, 'name' => 'chimpanzee'],
['id' => 40, 'name' => 'meeting'],
['id' => 20, 'name' => 'dynasty'],
['id' => 50, 'name' => 'chocolate'],
['id' => 10, 'name' => 'bananas'],
['id' => 50, 'name' => 'fantasy'],
['id' => 50, 'name' => 'football'],
];
// Grouping
$groupedItems = array_reduce($items, function ($carry, $item) {
$carry[$item['id']][] = $item;
return $carry;
}, []);
// Sorting
ksort($groupedItems, SORT_NUMERIC);
print_r($groupedItems);
mickmackusa
Debido a cómo el algoritmo de clasificación de PHP trata las matrices multidimensionales: clasifica por tamaño, luego compara los elementos uno a la vez, en realidad puede usar una clasificación de conservación de claves en la entrada ANTES de la reestructuración. En la programación de estilo funcional, esto significa que no necesita declarar la matriz de resultados como una variable.
Código: (Manifestación)
asort($array);
var_export(
array_reduce(
array_keys($array),
function($result, $k) use ($array) {
$result[$array[$k]['id']][$k] = $array[$k];
return $result;
}
)
);
Debo decir que la programación funcional no es muy atractiva para esta tarea porque se deben conservar las claves de primer nivel.
A pesar de array_walk()
es más sucinto, aún requiere que la matriz de resultados se pase al cierre como una variable de referencia. (Manifestación)
asort($array);
$result = [];
array_walk(
$array,
function($row, $k) use (&$result) {
$result[$row['id']][$k] = $row;
}
);
var_export($result);
Probablemente recomendaría un bucle clásico para esta tarea. Lo único que debe hacer el bucle es reorganizar las teclas de primer y segundo nivel. (Manifestación)
asort($array);
$result = [];
foreach ($array as $k => $row) {
$result[$row['id']][$k] = $row;
}
var_export($result);
Para ser completamente honesto, espero que ksort()
será más eficiente que la clasificación previa al bucle, pero quería una alternativa viable.