Is it possible in PHP to do something like this? How would you go about writing a function? Here is an example. The order is the most important thing.
$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = "this value doesn't need to be sorted";
And I'd like to do something like
$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));
Because at the end I use a foreach() and they're not in the right order (because I append the values to a string which needs to be in the correct order and I don't know in advance all of the array keys/values).
I've looked through PHP's internal array functions but it seems you can only sort alphabetically or numerically.
-
1VERY similar: Custom sort an associative array by its keys using a lookup array and Usort Without Replacing Keys PHPmickmackusa– mickmackusa ♦2023年05月22日 06:41:18 +00:00Commented May 22, 2023 at 6:41
16 Answers 16
Just use array_merge
or array_replace
. array_merge
works by starting with the array you give it (in the proper order) and overwriting/adding the keys with data from your actual array:
$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',
// 'dob' => '12/08/1986',
// 'address' => '123 fake st',
// 'dontSortMe' => 'this value doesnt need to be sorted')
PS: I'm answering this 'stale' question, because I think all the loops given as previous answers are overkill.
-
39It works nicely if you have string keys but not for the numerical one. PHP Docs: "If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended."bolbol– bolbol2012年11月07日 06:52:53 +00:00Commented Nov 7, 2012 at 6:52
-
9Nice, but what if the keys do not exist in the values? I need this, but only if any of the keys exist... Probably need a foreach on it then...Solomon Closson– Solomon Closson2014年05月08日 22:49:10 +00:00Commented May 8, 2014 at 22:49
-
7for my case it's array_replace instead of array_merge. array_merge combine both value instead of replacing the second array into the ordered keys.neofreko– neofreko2014年11月12日 06:26:48 +00:00Commented Nov 12, 2014 at 6:26
-
3I stumbled across your solution a couple of years ago while searching for something different - and I thought to myself, this is extremely efficient compared to the loops. Now I have a need for your solution and it took me an hour of searching to find it again! Thanks!Michael– Michael2017年11月13日 03:29:52 +00:00Commented Nov 13, 2017 at 3:29
-
5Additionally, if 'order' array (i.e., array('name', 'dob', 'address')) has more keys than the array to sort, then additional array_intersect of the the resulted sorted array with the original array would cut off stray keys that were added at array_merge.bbe– bbe2018年01月08日 10:24:54 +00:00Commented Jan 8, 2018 at 10:24
There you go:
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;
}
-
12So you can join 2 arrays with a + sign? I never knew that, I've been using
array_merge()
!alex– alex2009年09月24日 23:08:39 +00:00Commented Sep 24, 2009 at 23:08 -
5Is this better than using
usort()
oruasort()
?grantwparks– grantwparks2009年09月25日 19:57:08 +00:00Commented Sep 25, 2009 at 19:57 -
5You should insert a
break
statement once value has been found.Adel– Adel2011年09月26日 14:50:40 +00:00Commented Sep 26, 2011 at 14:50 -
7@alex Be very careful when you replace
array_merge()
with the array+
operator. It merges by key (also for numeric keys) and from left to right, whilearray_merge
merges from right to left and never overwrites numeric keys. E.g.[0,1,2]+[0,5,6,7] = [0,1,2,7]
whilearray_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]
and['a' => 5] + ['a' => 7] = ['a' => 5]
butarray_merge(['a' => 5], ['a' => 7]) = ['a' => 7]
.flu– flu2015年08月05日 08:07:33 +00:00Commented Aug 5, 2015 at 8:07 -
1The
+
or union operator is perfectly valid according to PHP Documentation in some use cases it's exactly what you want if you have to keep the numeric keys.Hexodus– Hexodus2017年05月12日 21:14:25 +00:00Commented May 12, 2017 at 21:14
How about this 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));
});
-
6This is not very efficient. For every comparison, two linear searches in the array are performed. If we assume the time complexity of uksort() to be
O(n * log n)
, then this algorithm runs inO(n^2 * log(n))
.TheOperator– TheOperator2020年01月23日 09:12:47 +00:00Commented Jan 23, 2020 at 9:12 -
This solutions work pretty well for arrays missing a value from the sort order. For small arrays the complexity is probably negligible. The solutions with multiple array functions might be even worse.2ndkauboy– 2ndkauboy2021年07月27日 15:56:37 +00:00Commented Jul 27, 2021 at 15:56
-
just
array_flip
the $order beforeuse
, and it goes fasterV.Volkov– V.Volkov2022年08月06日 06:59:57 +00:00Commented Aug 6, 2022 at 6:59 -
@V.Volkov But in that case you'll get a wrong resultPeter de Groot– Peter de Groot2022年08月07日 07:13:00 +00:00Commented Aug 7, 2022 at 7:13
-
1@PeterdeGroot sorry for not clarifying :)
$order = array_flip( $order );
before theuksort
andreturn $order[ $key1 ] > $order[ $key2 ];
insideuksort
V.Volkov– V.Volkov2022年08月07日 07:47:06 +00:00Commented Aug 7, 2022 at 7:47
Another way for 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);
Result:
Array (
[name] => Tim
[dob] => 12/08/1986
[address] => 123 fake st
[dontSortMe] => this value doesnt need to be sorted
)
Works fine with string and numeric keys.
-
3+ While they both work, I found
array_replace()
to relay intent better thanarray_merge()
.Jason McCreary– Jason McCreary2014年02月07日 15:25:12 +00:00Commented Feb 7, 2014 at 15:25 -
1
array_replace
also leaves the variable type intact. If one of the values in your array would have been(string) '1'
and you'd have used the+
operator, the value would have been turned into(int) 1
halfpastfour.am– halfpastfour.am2014年10月17日 09:24:39 +00:00Commented Oct 17, 2014 at 9:24 -
1This also works on numeric keys (
array_merge()
would just append them?). The logic is very well explained here. First,array_flip()
changes the $order array's values to keys. Second,array_replace()
replaces the values of the first array with values having the same keys in the second array. If you need to sort an array according to keys from another, you don't even have to usearray_flip
.ᴍᴇʜᴏᴠ– ᴍᴇʜᴏᴠ2017年12月14日 11:58:38 +00:00Commented Dec 14, 2017 at 11:58
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;
}
I used the Darkwaltz4's solution but used array_fill_keys
instead of array_flip
, to fill with NULL
if a key is not set in $array
.
$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
-
To remove items from $array which not listed in $keys I've used:
array_replace(array_fill_keys($keys, null), array_intersect_key($array, array_flip($keys)));
VladSavitsky– VladSavitsky2021年01月22日 13:49:37 +00:00Commented Jan 22, 2021 at 13:49 -
I don't know when I upvoted this, but I'm back, and wish I could upvote again :D Unlike any solution using
array_flip()
This answer works when the values are not scalars.jessica– jessica2022年01月19日 21:57:41 +00:00Commented Jan 19, 2022 at 21:57
Take one array as your order:
$order = array('north', 'east', 'south', 'west');
You can sort another array based on values using array_intersect
Docs:
/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);
Or in your case, to sort by keys, use array_intersect_key
Docs:
/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);
Both functions will keep the order of the first parameter and will only return the values (or keys) from the second array.
So for these two standard cases you don't need to write a function on your own to perform the sorting/re-arranging.
To apply this on an associative array (like the customer in the question)
, we can intersect on the keys and replace using array_replace
Docs with the original array.
That works, because associative arrays in PHP are ordered (compare: Are PHP Associative Arrays ordered? (Q&A)):
$order = array('name', 'dob', 'address', 'optional');
$sorted = array_replace(array_intersect_key(array_flip($order), $customer), $customer);
print_r($sorted);
This allows keeping the original order of optional keys.
Array
(
[name] => Tim
[dob] => 12/08/1986
[address] => 123 fake st
[dontSortMe] => this value doesn't need to be sorted
)
And it allows ignoring order values that are optional keys in the array.
If the latter is not a requirement, instead of sorting, the defaults having associative array can be replaced into as it is already an ordered map and customer will follow its order:
$order = array('name', 'dob', 'address', 'optional');
$sorted = array_replace(array_fill_keys($order, null), $customer);
print_r($sorted);
Array
(
[name] => Tim
[dob] => 12/08/1986
[address] => 123 fake st
[optional] =>
[dontSortMe] => this value doesn't need to be sorted
)
The original requirement that the dontSortMe key has to be preserved may suggest the former or the latter application.
Non-sorted defaults could otherwise be appended by a union:
$order = array('name', 'dob', 'address', 'optional');
$sorted = array_replace(array_intersect_key(array_flip($order), $customer), $customer);
print_r($sorted + array_fill_keys($order, null));
Array
(
[name] => Tim
[dob] => 12/08/1986
[address] => 123 fake st
[dontSortMe] => this value doesn't need to be sorted
[optional] =>
)
-
1The intersect would get rid of those entries he doesn't know in advance.DanMan– DanMan2013年03月06日 15:02:46 +00:00Commented Mar 6, 2013 at 15:02
-
1This is incorrect for sorting by keys. array_intersect_key will only return the values from array1, not array2spooky– spooky2015年01月21日 14:30:59 +00:00Commented Jan 21, 2015 at 14:30
-
Agreed with pavsid - the array_intersect_key example is incorrect - it returns the values from the first array, not the second.Jonathan Aquino– Jonathan Aquino2015年04月15日 03:18:47 +00:00Commented Apr 15, 2015 at 3:18
-
@DanMan: Which is what has been asked for. However as other comments also show, it was not enough of an answer for all of us, therefore I added three further permutations to sort/preserve on associative arrays, with or without default values for sort keys (and their different weight compared to the weight in the other associative array)hakre– hakre2025年02月02日 13:16:06 +00:00Commented Feb 2 at 13:16
Without magic...
$items=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);
SortByKeyList($items,$seq) result: array(5=>a,4=>b,28=>c);
function sortByKeyList($items,$seq){
$ret=array();
if(empty($items) || empty($seq)) return false;
foreach($seq as $key){$ret[$key]=$items[$key];}
return $ret;
}
-
4This works nice thanks, just update
$dataset
to match parameter namekursus– kursus2018年09月20日 10:37:40 +00:00Commented Sep 20, 2018 at 10:37 -
1This answer has no magic, no explanation, no defined constants, no adherence to PSR-12 coding guidelines, and will conditionally return
false
instead of an array.2023年05月22日 06:25:03 +00:00Commented May 22, 2023 at 6:25
IF you have array in your array, you'll have to adapt the function by Eran a little bit...
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;
}
PHP has functions to help you with this:
$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 does all the work for you and array_search provides the keys. array_search() returns false when it can't find a match so items that are not in the sort array naturally move to the bottom of the array.
Note: uasort() will order the array without affecting the key => value relationships.
- sort as requested
- save for int-keys (because of array_replace)
- don't return keys are not existing in inputArray
- (optionally) filter keys no existing in given keyList
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;
}
This function return a sub and sorted array based in second parameter $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));
}
Example:
$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];
A bit late, but I couldn't find the way I implemented it, this version needs closure, php>=5.3, but could be altered not to:
$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);
Of course 'dontSortMe' needs to be sorted out, and may appear first in the example
Output from above snippet: (Demo)
Warning: Undefined array key "dontSortMe" in /in/Q4osh on line 12
Warning: Undefined array key "dontSortMe" in /in/Q4osh on line 12
Warning: Undefined array key "dontSortMe" in /in/Q4osh on line 12
Array
(
[name] => Tim
[dontSortMe] => this value doesnt need to be sorted
[dob] => 12/08/1986
[address] => 123 fake st
)
-
Doesn't work for me with uksort but does if it's usortLaurence Cope– Laurence Cope2023年09月06日 18:25:11 +00:00Commented Sep 6, 2023 at 18:25
First 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;
}
Second Suggestion
$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
I wanted to point out that both of these suggestions are awesome. However, they are apples and oranges. The difference? One is non-associative friendly and the other is associative friendly. If you are using 2 fully associative arrays then the array merge/flip will actually merge and overwrite the other associative array. In my case that is not the results I was looking for. I used a settings.ini file to create my sort order array. The data array I was sorting did not need to written over by my associative sorting counterpart. Thus array merge would destroy my data array. Both are great methods, both need to be archived in any developers toolbox. Based on your needs you may find you actually need both concepts in your archives.
-
Sooooo... your first suggestion is this earlier posted answer and your second suggestion is this earlier posted answer It seems sensible to clarify where "your suggestions" came from.2023年05月22日 06:35:16 +00:00Commented May 22, 2023 at 6:35
I adopted the answer from @Darkwaltz4 for its brevity and would like to share how I adapted the solution to situations where the array may contain different keys for each iteration like so:
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';
and maintained a "master key" like so:
$master_key = array( 'dob' => ' ' , 'some_key' => ' ' );
array_merge would have executed the merge in the Array[1] iteration based on $master_key and produced ['some_key'] = '', an empty value, for that iteration. Hence, array_intersect_key was used to modify $master_key in each iterations like so:
foreach ($customer as $customer) {
$modified_key = array_intersect_key($master_key, $unordered_array);
$properOrderedArray = array_merge($modified_key, $customer);
}
If you have arrays like this, and you need to sort your array based on order, you can easily use this code:
$order = ['a', 'b', 'c', 'd', 'e'];
$needToSortArray = ['d', 'c', 'e'];
uksort($needToSortArray, function($key1, $key2) use ($order, $needToSortArray) {
return (array_search($needToSortArray[$key1], $order) > array_search($needToSortArray[$key2], $order));
});
Explore related questions
See similar questions with these tags.