groupe de tableaux php
J'ai le tableau suivant
Array
(
[0] => Array
(
[id] => 96
[shipping_no] => 212755-1
[part_no] => reterty
[description] => tyrfyt
[packaging_type] => PC
)
[1] => Array
(
[id] => 96
[shipping_no] => 212755-1
[part_no] => dftgtryh
[description] => dfhgfyh
[packaging_type] => PC
)
[2] => Array
(
[id] => 97
[shipping_no] => 212755-2
[part_no] => ZeoDark
[description] => s%c%s%c%s
[packaging_type] => PC
)
)
Comment puis-je regrouper le tableau par id
? Y a-t-il des fonctions PHP natives disponibles pour le faire?
Si je foreach
ce qui précède alors je vais obtenir un double, Comment puis-je éviter cela?
Sur l'exemple ci-dessus id
ont 2 éléments, donc son besoin d'être à l'intérieur du id
.
EDIT: TOUT FONCTIONNE BIEN: MAIS EST - IL POSSIBLE D'OBTENIR LA MÊME CHOSE AVEC UN FOREACH ?
16 réponses
Il n'est pas natif, il suffit d'utiliser une boucle.
$result = array();
foreach ($data as $element) {
$result[$element['id']][] = $element;
}
Vous pouvez essayer ce qui suit:
$group = array();
foreach ( $array as $value ) {
$group[$value['id']][] = $value;
}
var_dump($group);
Sortie:
array
96 =>
array
0 =>
array
'id' => int 96
'shipping_no' => string '212755-1' (length=8)
'part_no' => string 'reterty' (length=7)
'description' => string 'tyrfyt' (length=6)
'packaging_type' => string 'PC' (length=2)
1 =>
array
'id' => int 96
'shipping_no' => string '212755-1' (length=8)
'part_no' => string 'dftgtryh' (length=8)
'description' => string 'dfhgfyh' (length=7)
'packaging_type' => string 'PC' (length=2)
97 =>
array
0 =>
array
'id' => int 97
'shipping_no' => string '212755-2' (length=8)
'part_no' => string 'ZeoDark' (length=7)
'description' => string 's%c%s%c%s' (length=9)
'packaging_type' => string 'PC' (length=2)
Je viens de jeter cela ensemble, inspiré par. Net LINQ
<?php
// callable type hint may be "closure" type hint instead, depending on php version
function array_group_by(array $arr, callable $key_selector) {
$result = array();
foreach ($arr as $i) {
$key = call_user_func($key_selector, $i);
$result[$key][] = $i;
}
return $result;
}
$data = array(
array(1, "Andy", "PHP"),
array(1, "Andy", "C#"),
array(2, "Josh", "C#"),
array(2, "Josh", "ASP"),
array(1, "Andy", "SQL"),
array(3, "Steve", "SQL"),
);
$grouped = array_group_by($data, function($i){ return $i[0]; });
var_dump($grouped);
?>
Et voila vous obtenez
array(3) {
[1]=>
array(3) {
[0]=>
array(3) {
[0]=>
int(1)
[1]=>
string(4) "Andy"
[2]=>
string(3) "PHP"
}
[1]=>
array(3) {
[0]=>
int(1)
[1]=>
string(4) "Andy"
[2]=>
string(2) "C#"
}
[2]=>
array(3) {
[0]=>
int(1)
[1]=>
string(4) "Andy"
[2]=>
string(3) "SQL"
}
}
[2]=>
array(2) {
[0]=>
array(3) {
[0]=>
int(2)
[1]=>
string(4) "Josh"
[2]=>
string(2) "C#"
}
[1]=>
array(3) {
[0]=>
int(2)
[1]=>
string(4) "Josh"
[2]=>
string(3) "ASP"
}
}
[3]=>
array(1) {
[0]=>
array(3) {
[0]=>
int(3)
[1]=>
string(5) "Steve"
[2]=>
string(3) "SQL"
}
}
}
Dans un style de programmation plus fonctionnel, vous pouvez utiliser array_reduce
$groupedById = array_reduce($data, function (array $accumulator, array $element) {
$accumulator[$element['id']][] = $element;
return $accumulator;
}, []);
J'avais aussi besoin d'une telle fonction. J'ai créé celui-ci, vous passez quelle colonne vous voulez regrouper par:
function array_group(array $data, $by_column)
{
$result = [];
foreach ($data as $item) {
$column = $item[$by_column];
unset($item[$by_column]);
if (isset($result[$column])) {
$result[$column][] = $item;
} else {
$result[$column] = array($item);
}
}
return $result;
}
for($i = 0 ; $i < count($arr) ; $i++ )
{
$tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
}
$vmpArr = array_keys($tmpArr);
print_r($vmpArr);
Cette fonction array_group_by réalise ce que vous recherchez:
$grouped = array_group_by($arr, 'id');
Il prend même en charge les regroupements à plusieurs niveaux:
$grouped = array_group_by($arr, 'id', 'part_no');
Expansion sur la réponse de @baba, que j'aime, mais crée un multi-dimensionnel profond à trois niveaux plus complexe (array (array(array))):
$group = array();
foreach ( $array as $value ) {
$group[$value['id']][] = $value;
}
// output only data from id 96
foreach ($group as $key=>$value) { //outer loop
foreach ($value as $k=>$v){ //inner loop
if($key==96){ //if outer loop is equal to 96 (could be variable)
for ($i=0;$i<count($k);$i++){ //iterate over the inner loop
printf($key.' has a part no. of '.$v['part_no'].' and shipping no. of '.$v['shipping_no'].'<br>');
}
}
}
}
Affichera:
96 a un numéro de pièce. de reterty et numéro d'expédition de 212755-1
96 a un numéro de pièce. de dftgtryh et numéro d'expédition de 212755-1
C'est trivial à voir avec LINQ, qui est implémenté en PHP dans plusieurs bibliothèques, y compris YaLinqo*. Il permet d'effectuer des requêtes de type SQL sur des tableaux et des objets. La fonction groubBy
est conçue spécifiquement pour le regroupement, il vous suffit de spécifier le champ que vous souhaitez regrouper par:
$grouped_array = from($array)->groupBy('$v["id"]')->toArray();
Où '$v["id"]'
est un raccourci pour function ($v) { return $v["id"]; }
, qui cette bibliothèque prend en charge.
Le résultat sera exactement comme dans la réponse acceptée, juste avec moins de code.
* développé par moi
1. GROUP BY
clé
Cette fonction fonctionne comme GROUP BY
pour le tableau, mais avec une restriction importante: un Seul groupement "colonne" ($identifier
) est possible.
function arrayUniqueByIdentifier(array $array, string $identifier)
{
$ids = array_column($array, $identifier);
$ids = array_unique($ids);
$array = array_filter($array,
function ($key, $value) use($ids) {
return in_array($value, array_keys($ids));
}, ARRAY_FILTER_USE_BOTH);
return $array;
}
2. Détection des lignes uniques pour une table (tableau bidimensionnel)
Cette fonction sert à filtrer les "lignes". Si nous disons, un tableau bidimensionnel est une table, alors chaque élément est une ligne. Ainsi, nous pouvons supprimer les lignes dupliquées avec cette fonction. Deux rangées (éléments du premier dimension) sont égaux, si toutes leurs colonnes (éléments de la deuxième dimension) sont égales. À la comparaison des valeurs" colonne " s'applique: si une valeur est d'un type simple , la valeur elle-même sera utilisée lors de la comparaison; sinon son type (array
, object
, resource
, unknown type
) sera utilisé.
La stratégie est simple: faire du tableau d'origine un tableau peu profond, où les éléments sont implode
d "colonnes" du tableau d'origine; puis appliquer array_unique(...)
dessus; et comme dernière utilisation des ID détectés pour le filtrage du tableau original.
function arrayUniqueByRow(array $table = [], string $implodeSeparator)
{
$elementStrings = [];
foreach ($table as $row) {
// To avoid notices like "Array to string conversion".
$elementPreparedForImplode = array_map(
function ($field) {
$valueType = gettype($field);
$simpleTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
$field = in_array($valueType, $simpleTypes) ? $field : $valueType;
return $field;
}, $row
);
$elementStrings[] = implode($implodeSeparator, $elementPreparedForImplode);
}
$elementStringsUnique = array_unique($elementStrings);
$table = array_intersect_key($table, $elementStringsUnique);
return $table;
}
Il est également possible d'améliorer la comparaison, en détectant la classe de la valeur "colonne", si son type est object
.
Le $implodeSeparator
devrait être plus ou moins complexe, z.B. spl_object_hash($this)
.
3. Détection des lignes avec des colonnes d'identifiant unique pour une table (tableau à deux dimensions)
Cette solution repose sur la 2ème. Maintenant la complète "ligne" n'a pas besoin d'être unique. Deux "lignes" (éléments de la première dimension) sont égal maintenant, si tous les "champs" pertinents (éléments de la deuxième dimension) d'une "ligne" sont égaux aux "champs" correspondants (éléments avec la même clé).
Les "champs ""pertinents" sont les " champs "(éléments de la deuxième dimension), qui ont une clé, qui est égale à l'un des éléments des "identificateurs"passés.
function arrayUniqueByMultipleIdentifiers(array $table, array $identifiers, string $implodeSeparator = null)
{
$arrayForMakingUniqueByRow = $removeArrayColumns($table, $identifiers, true);
$arrayUniqueByRow = $arrayUniqueByRow($arrayForMakingUniqueByRow, $implodeSeparator);
$arrayUniqueByMultipleIdentifiers = array_intersect_key($table, $arrayUniqueByRow);
return $arrayUniqueByMultipleIdentifiers;
}
function removeArrayColumns(array $table, array $columnNames, bool $isWhitelist = false)
{
foreach ($table as $rowKey => $row) {
if (is_array($row)) {
if ($isWhitelist) {
foreach ($row as $fieldName => $fieldValue) {
if (!in_array($fieldName, $columnNames)) {
unset($table[$rowKey][$fieldName]);
}
}
} else {
foreach ($row as $fieldName => $fieldValue) {
if (in_array($fieldName, $columnNames)) {
unset($table[$rowKey][$fieldName]);
}
}
}
}
}
return $table;
}
$arr = array();
foreach($old_arr as $key => $item)
{
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
Cela devrait regrouper un tableau associatif Groupe Ejm Par Pays
function getGroupedArray($array, $keyFieldsToGroup) {
$newArray = array();
foreach ($array as $record)
$newArray = getRecursiveArray($record, $keyFieldsToGroup, $newArray);
return $newArray;
}
function getRecursiveArray($itemArray, $keys, $newArray) {
if (count($keys) > 1)
$newArray[$itemArray[$keys[0]]] = getRecursiveArray($itemArray, array_splice($keys, 1), $newArray[$itemArray[$keys[0]]]);
else
$newArray[$itemArray[$keys[0]]][] = $itemArray;
return $newArray;
}
$countries = array(array('Country'=>'USA', 'State'=>'California'),
array('Country'=>'USA', 'State'=>'Alabama'),
array('Country'=>'BRA', 'State'=>'Sao Paulo'));
$grouped = getGroupedArray($countries, array('Country'));
function array_group_by($arr, array $keys) {
if (!is_array($arr)) {
trigger_error('array_group_by(): The first argument should be an array', E_USER_ERROR);
}
if (count($keys)==0) {
trigger_error('array_group_by(): The Second argument Array can not be empty', E_USER_ERROR);
}
// Load the new array, splitting by the target key
$grouped = [];
foreach ($arr as $value) {
$grouped[$value[$keys[0]]][] = $value;
}
// Recursively build a nested grouping if more parameters are supplied
// Each grouped array value is grouped according to the next sequential key
if (count($keys) > 1) {
foreach ($grouped as $key => $value) {
$parms = array_merge([$value], [array_slice($keys, 1,count($keys))]);
$grouped[$key] = call_user_func_array('array_group_by', $parms);
}
}
return $grouped;
}
$arr = Données Araay;
$ fldName = groupe par nom de colonne;
function array_group_by( $arr, $fldName) {
$groups = array();
foreach ($arr as $rec) {
$groups[$rec[$fldName]] = $rec;
}
return $groups;
}
function object_group_by( $obj, $fldName) {
$groups = array();
foreach ($obj as $rec) {
$groups[$rec->$fldName] = $rec;
}
return $groups;
}
function groupeByPHP($array,$indexUnique,$assoGroup,$keepInOne){
$retour = array();
$id = $array[0][$indexUnique];
foreach ($keepInOne as $keep){
$retour[$id][$keep] = $array[0][$keep];
}
foreach ($assoGroup as $cle=>$arrayKey){
$arrayGrouped = array();
foreach ($array as $data){
if($data[$indexUnique] != $id){
$id = $data[$indexUnique];
foreach ($keepInOne as $keep){
$retour[$id][$keep] = $data[$keep];
}
}
foreach ($arrayKey as $val){
$arrayGrouped[$val] = $data[$val];
}
$retour[$id][$cle][] = $arrayGrouped;
$retour[$id][$cle] = array_unique($retour[$id][$cle],SORT_REGULAR);
}
}
return $retour;
}
Essayez cette fonction
groupeByPHP($yourArray,'id',array('desc'=>array('part_no','packaging_type')),array('id','shipping_no'))