Trier un tableau par clés en fonction d'un autre tableau?

153

Est-il possible en PHP de faire quelque chose comme ça? Comment feriez-vous pour écrire une fonction? Voici un exemple. L'ordre est la chose la plus importante.

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

Et j'aimerais faire quelque chose comme

$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));

Parce qu'à la fin j'utilise un foreach () et ils ne sont pas dans le bon ordre (car j'ajoute les valeurs à une chaîne qui doit être dans le bon ordre et je ne connais pas à l'avance toutes les clés du tableau / valeurs).

J'ai parcouru les fonctions de tableau interne de PHP, mais il semble que vous ne pouvez trier que par ordre alphabétique ou numérique.

Alex
la source

Réponses:

348

Utilisez simplement array_mergeou array_replace. Array_mergefonctionne en commençant par le tableau que vous lui donnez (dans le bon ordre) et en écrasant / ajoutant les clés avec les données de votre tableau actuel:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
//Or:
$properOrderedArray = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

//$properOrderedArray -> array('name' => 'Tim', 'address' => '123 fake st', 'dob' => '12/08/1986', 'dontSortMe' => 'this value doesnt need to be sorted')

ps - Je réponds à cette question «périmée», car je pense que toutes les boucles données comme réponses précédentes sont exagérées.

Darkwaltz4
la source
31
Cela fonctionne bien si vous avez des clés de chaîne mais pas pour les touches numériques. PHP Docs: "Si les tableaux d'entrée ont les mêmes clés de chaîne, alors la dernière valeur de cette clé écrasera la précédente. Si, cependant, les tableaux contiennent des clés numériques, la dernière valeur n'écrasera pas la valeur d'origine, mais sera ajouté. "
bolbol
7
Bien, mais que faire si les clés n'existent pas dans les valeurs? J'en ai besoin, mais seulement si l'une des clés existe ... Probablement besoin d'un foreach dessus alors ...
Solomon Closson
5
dans mon cas, c'est array_replace au lieu de array_merge. array_merge combine les deux valeurs au lieu de remplacer le deuxième tableau dans les clés ordonnées.
neofreko
3
Je suis tombé sur votre solution il y a quelques années en cherchant quelque chose de différent - et je me suis dit que c'était extrêmement efficace par rapport aux boucles. J'ai maintenant besoin de votre solution et il m'a fallu une heure de recherche pour la retrouver! Merci!
Michael
4
De plus, si le tableau 'order' (c'est-à-dire, array ('name', 'dob', 'address')) a plus de clés que le tableau à trier, alors array_intersect supplémentaire du tableau trié résultant avec le tableau d'origine serait coupé clés parasites qui ont été ajoutées à array_merge.
avant le
105

Voilà:

function sortArrayByArray(array $array, array $orderArray) {
    $ordered = array();
    foreach ($orderArray as $key) {
        if (array_key_exists($key, $array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}
Eran Galperin
la source
12
Vous pouvez donc joindre 2 tableaux avec un signe +? Je n'ai jamais su ça, je l'ai utilisé array_merge()!
alex
3
Est-ce mieux que d'utiliser usort()ou uasort()?
grantwparks
5
Vous devez insérer une breakinstruction une fois que la valeur a été trouvée.
Adel le
4
@alex Soyez très prudent lorsque vous remplacez array_merge()par l' +opérateur de tableau . Il fusionne par clé (également pour les touches numériques) et de gauche à droite , tandis que array_mergefusionne de droite à gauche et n'écrase jamais les touches numériques. Par exemple, [0,1,2]+[0,5,6,7] = [0,1,2,7]pendant array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]et ['a' => 5] + ['a' => 7] = ['a' => 5]mais array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
grippe
Est-il sécuritaire d'utiliser le +signe?
crmpicco
47

Que diriez-vous de cette solution

$order = array(1,5,2,4,3,6);

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six'
);

uksort($array, function($key1, $key2) use ($order) {
    return (array_search($key1, $order) > array_search($key2, $order));
});
Peter de Groot
la source
1
Celui-ci a besoin de plus de votes positifs, d'autres ne fonctionnent pas / ne fonctionnent pas bien alors que cela fonctionne bien dans mon cas.
Ng Sek Long
Ce n'est pas très efficace. Pour chaque comparaison, deux recherches linéaires dans le tableau sont effectuées. Si nous supposons que la complexité temporelle de uksort () est O(n * log n), alors cet algorithme s'exécute O(n^2 * log(n)).
TheOperator
36

Une autre façon pour PHP> = 5.3.0:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$customerSorted = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

Résultat:

Array (
  [name] => Tim
  [dob] => 12/08/1986
  [address] => 123 fake st
  [dontSortMe] => this value doesnt need to be sorted
)

Fonctionne très bien avec les touches de chaîne et numériques.

ekuser
la source
2
+ Bien qu'ils fonctionnent tous les deux, j'ai trouvé array_replace()qu'il était préférable de transmettre l'intention array_merge().
Jason McCreary
1
array_replacelaisse également le type de variable intact. Si l'une des valeurs de votre tableau aurait été (string) '1'et que vous auriez utilisé l' +opérateur, la valeur aurait été transformée en(int) 1
halfpastfour.am
1
Cela fonctionne également sur les touches numériques (les array_merge()ajouteraient-elles simplement?). La logique est très bien expliquée ici . Tout d'abord , array_flip()change les valeurs du tableau $ order en clés. Deuxièmement , array_replace()remplace les valeurs du premier tableau par des valeurs ayant les mêmes clés dans le deuxième tableau. Si vous devez trier un tableau en fonction des clés d'un autre, vous n'avez même pas à utiliser array_flip.
aexl
23
function sortArrayByArray(array $toSort, array $sortByValuesAsKeys)
{
    $commonKeysInOrder = array_intersect_key(array_flip($sortByValuesAsKeys), $toSort);
    $commonKeysWithValue = array_intersect_key($toSort, $commonKeysInOrder);
    $sorted = array_merge($commonKeysInOrder, $commonKeysWithValue);
    return $sorted;
}
OIS
la source
14

Prenez un tableau comme votre commande:

$order = array('north', 'east', 'south', 'west');

Vous pouvez trier un autre tableau en fonction des valeurs à l'aide de array_intersectDocs :

/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);

Ou dans votre cas, pour trier par clés, utilisez array_intersect_keyDocs :

/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);

Les deux fonctions conserveront l'ordre du premier paramètre et ne renverront que les valeurs (ou clés) du second tableau.

Donc, pour ces deux cas standards, vous n'avez pas besoin d'écrire une fonction vous-même pour effectuer le tri / réarrangement.

hakre
la source
L'intersection éliminerait ces entrées qu'il ne connaît pas à l'avance.
DanMan
1
Ceci est incorrect pour le tri par clés. array_intersect_key ne renverra que les valeurs de array1, pas de array2
spooky
D'accord avec pavsid - l'exemple array_intersect_key est incorrect - il renvoie les valeurs du premier tableau, pas du second.
Jonathan Aquino
10

J'ai utilisé la solution de Darkwaltz4 mais utilisé à la array_fill_keysplace de array_flip, pour remplir NULLsi une clé n'est pas définie $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
Baptiste Bernard
la source
5

Sans magie ...

$array=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);    
SortByKeyList($array,$seq) result: array(5=>a,4=>b,28=>c);

function sortByKeyList($array,$seq){
    $ret=array();
    if(empty($array) || empty($seq)) return false;
    foreach($seq as $key){$ret[$key]=$dataset[$key];}
    return $ret;
}
Jenovai Matyas
la source
1
Cela fonctionne bien merci, il suffit de mettre $datasetà jour pour correspondre au nom du paramètre
kursus
3

SI vous avez un tableau dans votre tableau, vous devrez adapter un peu la fonction d'Eran ...

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key => $value) {
        if(array_key_exists($key,$array)) {
                $ordered[$key] = $array[$key];
                unset($array[$key]);
        }
    }
    return $ordered + $array;
}
Boombastic
la source
2

Cette fonction renvoie un sous-tableau trié basé sur le deuxième paramètre $ keys

function array_sub_sort(array $values, array $keys){
    $keys = array_flip($keys);
    return array_merge(array_intersect_key($keys, $values), array_intersect_key($values, $keys));
}

Exemple:

$array_complete = [
    'a' => 1,
    'c' => 3,
    'd' => 4,
    'e' => 5,
    'b' => 2
];

$array_sub_sorted = array_sub_sort($array_complete, ['a', 'b', 'c']);//return ['a' => 1, 'b' => 2, 'c' => 3];
Doglas
la source
1

PHP a des fonctions pour vous aider:

$arrayToBeSorted = array('west', 'east', 'south', 'north');
$order = array('north', 'south', 'east', 'west');

// sort array
usort($arrayToBeSorted, function($a, $b) use ($order){
    // sort using the numeric index of the second array
    $valA = array_search($a, $order);
    $valB = array_search($b, $order);

    // move items that don't match to end
    if ($valA === false)
        return -1;
    if ($valB === false)
        return 0;

    if ($valA > $valB)
        return 1;
    if ($valA < $valB)
        return -1;
    return 0;
});

Usort fait tout le travail pour vous et array_search fournit les clés. array_search () renvoie false quand il ne trouve pas de correspondance, donc les éléments qui ne sont pas dans le tableau de tri se déplacent naturellement vers le bas du tableau.

Remarque: uasort () ordonnera le tableau sans affecter les relations clé => valeur.

danielcraigie
la source
1
  • trier comme demandé
  • enregistrer pour les clés int (à cause de array_replace)
  • Ne pas retourner les clés n'existent pas dans inputArray
  • (facultatif) les clés de filtre n'existent pas dans la liste de clés donnée

Code:

 /**
 * sort keys like in key list
 * filter: remove keys are not listed in keyList
 * ['c'=>'red', 'd'=>'2016-12-29'] = sortAndFilterKeys(['d'=>'2016-12-29', 'c'=>'red', 'a'=>3 ]], ['c', 'd', 'z']){
 *
 * @param array $inputArray
 * @param string[]|int[] $keyList
 * @param bool $removeUnknownKeys
 * @return array
 */
static public function sortAndFilterKeys($inputArray, $keyList, $removeUnknownKeys=true){
    $keysAsKeys = array_flip($keyList);
    $result = array_replace($keysAsKeys, $inputArray); // result = sorted keys + values from input + 
    $result = array_intersect_key($result, $inputArray); // remove keys are not existing in inputArray 
    if( $removeUnknownKeys ){
        $result = array_intersect_key($result, $keysAsKeys); // remove keys are not existing in keyList 
    }
    return $result;
}
Grain
la source
1

Première suggestion

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key) {
        if(array_key_exists($key,$array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

Deuxième suggestion

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);

Je voulais souligner que ces deux suggestions sont géniales. Cependant, ce sont des pommes et des oranges. La différence? L'un est convivial non associatif et l'autre est convivial associatif. Si vous utilisez 2 tableaux entièrement associatifs, le tableau fusion / retournement fusionnera et écrasera en fait l'autre tableau associatif. Dans mon cas, ce ne sont pas les résultats que je recherchais. J'ai utilisé un fichier settings.ini pour créer mon tableau d'ordre de tri. Le tableau de données que je triais n'avait pas besoin d'être écrasé par mon homologue de tri associatif. Ainsi, la fusion de tableaux détruirait mon tableau de données. Les deux sont d'excellentes méthodes, les deux doivent être archivées dans la boîte à outils de n'importe quel développeur. En fonction de vos besoins, vous constaterez peut-être que vous avez réellement besoin des deux concepts dans vos archives.

user1653711
la source
1

J'ai adopté la réponse de @ Darkwaltz4 pour sa brièveté et je voudrais partager comment j'ai adapté la solution aux situations où le tableau peut contenir différentes clés pour chaque itération comme ceci:

Array[0] ...
['dob'] = '12/08/1986';
['some_key'] = 'some value';

Array[1] ...
['dob'] = '12/08/1986';

Array[2] ...
['dob'] = '12/08/1986';
['some_key'] = 'some other value';

et a maintenu une "clé principale" comme ceci:

$master_key = array( 'dob' => ' ' ,  'some_key' => ' ' );

array_merge aurait exécuté la fusion dans l'itération Array [1] basée sur $ master_key et produit ['some_key'] = '', une valeur vide, pour cette itération. Par conséquent, array_intersect_key a été utilisé pour modifier $ master_key à chaque itération comme ceci:

foreach ($customer as $customer) {
  $modified_key = array_intersect_key($master_key, $unordered_array);
  $properOrderedArray = array_merge($modified_key, $customer);
}
Studio Pageii
la source
0

Un peu tard, mais je n'ai pas trouvé la manière dont je l'ai implémentée, cette version doit être fermée, php> = 5.3, mais pourrait être modifiée pour ne pas:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$order = array('name', 'dob', 'address');

$keys= array_flip($order);
uksort($customer, function($a, $b)use($keys){
    return $keys[$a] - $keys[$b];
});
print_r($customer);

Bien sûr, 'dontSortMe' doit être trié et peut apparaître en premier dans l'exemple

DJules
la source