105 votes

Trier un tableau multidimensionnel par plusieurs colonnes

Je cherche à trier un tableau multidimensionnel selon plusieurs clés, et je n'ai aucune idée par où commencer. J'ai regardé uasort(), mais je n'étais pas sûr comment écrire une fonction pour ce dont j'ai besoin.

Je dois trier par le state, ensuite par event_type, puis par date_start.

Mon tableau ressemble à ceci :

[
    ['ID' => 1, 'title' => 'Boring Meeting',  'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Mario Party',     'date_start' => '2010-07-22', 'event_type' => 'party',   'state' => 'new-york'],
    ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party',   'state' => 'california']
]

Mon résultat souhaité est :

[
    ['ID' => 4, 'title' => 'Duct Tape Party', 'date_start' => '2010-07-28', 'event_type' => 'party',   'state' => 'california']
    ['ID' => 2, 'title' => 'Find My Stapler', 'date_start' => '2010-07-22', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 1, 'title' => 'Boring Meeting',  'date_start' => '2010-07-30', 'event_type' => 'meeting', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Mario Party',     'date_start' => '2010-07-22', 'event_type' => 'party',   'state' => 'new-york'],
]

1 votes

…et comment souhaitez-vous le trier ?

0 votes

Possible duplicate de Tri multidimensionnel tableau en PHP

206voto

Rob Points 3582

Vous avez besoin de array_multisort

$mylist = array(
    array('ID' => 1, 'title' => 'Réunion ennuyeuse', 'event_type' => 'réunion'),
    array('ID' => 2, 'title' => 'Trouver ma agrafeuse', 'event_type' => 'réunion'),
    array('ID' => 3, 'title' => 'Mario Party', 'event_type' => 'fête'),
    array('ID' => 4, 'title' => 'Fête du ruban adhésif', 'event_type' => 'fête')
);

# obtenir une liste des colonnes de tri et de leurs données à passer à array_multisort
$sort = array();
foreach($mylist as $k=>$v) {
    $sort['title'][$k] = $v['title'];
    $sort['event_type'][$k] = $v['event_type'];
}
# trier par event_type desc et ensuite par title asc
array_multisort($sort['event_type'], SORT_DESC, $sort['title'], SORT_ASC,$mylist);

À partir de PHP 5.5.0:

array_multisort(array_column($mylist, 'event_type'), SORT_DESC,
                array_column($mylist, 'title'),      SORT_ASC,
                $mylist);

$mylist est maintenant:

array (
  0 => 
  array (
    'ID' => 4,
    'title' => 'Fête du ruban adhésif',
    'event_type' => 'fête',
  ),
  1 => 
  array (
    'ID' => 3,
    'title' => 'Mario Party',
    'event_type' => 'fête',
  ),
  2 => 
  array (
    'ID' => 1,
    'title' => 'Réunion ennuyeuse',
    'event_type' => 'réunion',
  ),
  3 => 
  array (
    'ID' => 2,
    'title' => 'Trouver ma agrafeuse',
    'event_type' => 'réunion',
  ),
)

0 votes

@Rob je suis très curieux de savoir comment tu trierais date_start

7 votes

Qu'est-ce qu'une "fête du ruban adhésif" ?

0 votes

Pour PHP < 5.5 il y a un polyfill pour la fonction array_column github.com/ramsey/array_column. Il est donc possible d'utiliser une méthode plus élégante à partir du deuxième extrait de code sur les anciennes versions.

44voto

mickmackusa Points 18931

PHP7 Rend le tri par plusieurs colonnes SUPER facile avec l'opérateur spaceship (<=>) alias l'opérateur de comparaison combiné ou l'opérateur de comparaison ternaire.

Ressource: https://wiki.php.net/rfc/combined-comparison-operator

Le tri par plusieurs colonnes est aussi simple que d'écrire des tableaux équilibrés / relationnels de chaque côté de l'opérateur. Facile à faire!

Lorsque la valeur $a est à gauche de l'opérateur spaceship et la valeur $b est à droite, le tri ASCendant est utilisé.

Lorsque la valeur $b est à gauche de l'opérateur spaceship et la valeur $a est à droite, le tri DESCendant est utilisé.

Lorsque l'opérateur spaceship compare deux chaînes numériques, il les compare comme des nombres -- vous obtenez donc un tri naturel automatiquement.

Je n'ai pas utilisé uasort() car je ne vois pas la nécessité de conserver les index d'origine.

Code: (Démo) -- trie par state ASC, puis par event_type ASC, puis par date_start ASC

$array = [
    ['ID' => 1, 'title' => 'Réunion Ennuyeuse', 'date_start' => '2010-07-30', 'event_type' => 'réunion', 'state' => 'new-york'],
    ['ID' => 2, 'title' => 'Trouver Ma Agrafeuse', 'date_start' => '2010-07-22', 'event_type' => 'réunion', 'state' => 'new-york'],
    ['ID' => 3, 'title' => 'Soirée Mario', 'date_start' => '2010-07-22', 'event_type' => 'party', 'state' => 'new-york'],
    ['ID' => 4, 'title' => 'Soirée du Ruban Adhésif', 'date_start' => '2010-07-28', 'event_type' => 'party', 'state' => 'california']
];

usort($array, function($a, $b) {
    return [$a['state'], $a['event_type'], $a['date_start']]
           <=>
           [$b['state'], $b['event_type'], $b['date_start']];
});

var_export($array);

Résultat

array (
  0 => 
  array (
    'ID' => 4,
    'title' => 'Soirée du Ruban Adhésif',
    'date_start' => '2010-07-28',
    'event_type' => 'party',
    'state' => 'california',
  ),
  1 => 
  array (
    'ID' => 2,
    'title' => 'Trouver Ma Agrafeuse',
    'date_start' => '2010-07-22',
    'event_type' => 'réunion',
    'state' => 'new-york',
  ),
  2 => 
  array (
    'ID' => 1,
    'title' => 'Réunion Ennuyeuse',
    'date_start' => '2010-07-30',
    'event_type' => 'réunion',
    'state' => 'new-york',
  ),
  3 => 
  array (
    'ID' => 3,
    'title' => 'Soirée Mario',
    'date_start' => '2010-07-22',
    'event_type' => 'party',
    'state' => 'new-york',
  ),
)

p.s. Syntaxe de la flèche avec PHP7.4 et supérieur (Démo)...

usort($array, fn($a, $b) =>
    [$a['state'], $a['event_type'], $a['date_start']]
    <=>
    [$b['state'], $b['event_type'], $b['date_start']]
);

La technique équivalente avec array_multisort() et un appel de array_column() pour chaque critère de tri est: (Démo)

array_multisort(
    array_column($array, 'state'),
    array_column($array, 'event_type'),
    array_column($array, 'date_start'),
    $array
);

18voto

Stijn Leenknegt Points 646

Vous pouvez le faire avec usort. L'argument $cmp_function pourrait être :

function my_sorter($a, $b) {
    $c = strcmp($a['state'], $b['state']);
    if($c != 0) {
        return $c;
    }

    $c = strcmp($a['event_type'], $b['event_type']);
    if($c != 0) {
        return $c;
    }

    return strcmp($a['date_start'], $b['date_start']);
}

Pour un nombre arbitraire de champs en PHP 5.3, vous pouvez utiliser des fermetures pour créer une fonction de comparaison :

function make_cmp($fields, $fieldcmp='strcmp') {
    return function ($a, $b) use (&$fields) {
        foreach ($fields as $field) {
            $diff = $fieldcmp($a[$field], $b[$field]);
            if($diff != 0) {
                return $diff;
            }
        }
        return 0;
    }
}

usort($arr, make_cmp(array('state', 'event_type', 'date_start')))

Pour un nombre arbitraire de champs de types différents en PHP 5.3 :

function make_cmp($fields, $dfltcmp='strcmp') {
    # assign array in case $fields has no elements
    $fieldcmps = array();
    # assign a comparison function to fields that aren't given one
    foreach ($fields as $field => $cmp) {
        if (is_int($field) && ! is_callable($cmp)) {
            $field = $cmp;
            $cmp = $dfltcmp;
        }
        $fieldcmps[$field] = $cmp;
    }
    return function ($a, $b) use (&$fieldcmps) {
        foreach ($fieldcmps as $field => $cmp) {
            $diff = call_user_func($cmp, $a[$field], $b[$field]);
            if($diff != 0) {
                return $diff;
            }
        }
        return 0;
    }
}

function numcmp($a, $b) {
    return $a - $b;
}
function datecmp($a, $b) {
    return strtotime($a) - strtotime($b);
}
/**
 * Higher priority come first; a priority of 2 comes before 1.
 */
function make_evt_prio_cmp($priorities, $default_priority) {
    return function($a, $b) use (&$priorities) {
        if (isset($priorities[$a])) {
            $prio_a = $priorities[$a];
        } else {
            $prio_a = $default_priority;
        }
        if (isset($priorities[$b])) {
            $prio_b = $priorities[$b];
        } else {
            $prio_b = $default_priority;
        }
        return $prio_b - $prio_a;
    };
}

$event_priority_cmp = make_evt_prio_cmp(
    array('meeting' => 5, 'party' => 10, 'concert' => 7), 
    0);

usort($arr, make_cmp(array('state', 'event' => $event_priority_cmp, 'date_start' => 'datecmp', 'id' => 'numcmp')))

1 votes

Vous pourriez simplifier considérablement l'imbrication, et je pense que vous devrez faire quelque chose de plus avec la date, mais l'approche semble la meilleure jusqu'à présent.

1 votes

La bonne chose à propos du format '%Y-%m-%d' utilisé dans le tableau d'exemple est que la comparaison de chaînes fonctionne pour la comparaison des dates.

3voto

Patel Points 377

J'ai essayé le code ci-dessous et j'ai réussi

code array

$chansons = array(
        '1' => array('artiste'=>'Smashing Pumpkins', 'titre_chanson'=>'Soma'),
        '2' => array('artiste'=>'The Decemberists', 'titre_chanson'=>'The Island'),
        '3' => array('artiste'=>'Fleetwood Mac', 'titre_chanson' =>'Second-hand News')
);

appeler la fonction de tri du tableau

$chansons = subval_sort($chansons,'artiste'); 
print_r($chansons);

fonction de tri du tableau

function subval_sort($a,$sous_cle) {
    foreach($a as $k=>$v) {
        $b[$k] = strtolower($v[$subkey]);
    }
    asort($b);
    foreach($b as $key=>$val) {
        $c[] = $a[$key];
    }
    return $c;
}

si la fonction de tri du tableau en ordre inverse

function subval_sort($a,$subkey) {
        foreach($a as $k=>$v) {
            $b[$k] = strtolower($v[$subkey]);
        }
        arsort($b);
        foreach($b as $key=>$val) {
            $c[] = $a[$key];
        }
        return $c;
    }

0 votes

Cela trie simplement par colonne (de manière peu élégante), mais la question de l'OP nécessite que le critère de tri implique trois valeurs de colonne. Au mieux, c'est la réponse correcte à une question différente.

2voto

ling Points 177

En améliorant le code de génie de @Stijn Leenknegt, voici ma fonction pragmatique à 2 cents:

$data[] = array('volume' => 67, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 1);
$data[] = array('volume' => 85, 'edition' => 6);
$data[] = array('volume' => 98, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 6);
$data[] = array('volume' => 67, 'edition' => 7);

function make_cmp(array $sortValues)
{
    return function ($a, $b) use (&$sortValues) {
        foreach ($sortValues as $column => $sortDir) {
            $diff = strcmp($a[$column], $b[$column]);
            if ($diff !== 0) {
                if ('asc' === $sortDir) {
                    return $diff;
                }
                return $diff * -1;
            }
        }
        return 0;
    };
}

usort($data, make_cmp(['volume' => "desc", 'edition' => "asc"]));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X