Say,$arr contains multiple sub-arrays with the key "pos",
$arr = sortByKey($arr,'pos');
After that the sub-array with smallest "pos" value will be ordered first,and so on.
EDIT
$sub1 = array('pos' => 2);
$sub2 = array('pos' => 1);
$arr = array($sub1,$sub2);
$arr = sortByKey($arr,'pos');
After this function,$arr will be array($sub2,$sub1)
see the ksort function.
here come the manual of the function.
sorry also I think you are ccase you are more looking into uasort you would be able to define a function to compare each of your elements and sort them
// Array to be sorted
print_r($array);
// Sort and print the resulting array
uasort($array, create_function('$a,$b', 'return $a[\'pos\'] == $b[\'pos\'] ? 0 : (($a[\'pos\'] < $b[\'pos\']) ? -1 : 1);'));
print_r($array);
have to be tested not sure about the double ? operator ...
Cheer
Let me write you a function. Its not tested.
function subarray_sort($array, $subkey) {
$sortarray=array();
foreach($array as $item) {
$sortarray[]=$item[$subkey];
}
array_multisort($sortarray, $array);
}
Related
I have an associative PHP array and I would like to generate a list of keys that pass a certain test. For example
$myArray = ('28'=>0.01,'51'=>-0.1,'48'=>0.4,'53'=>-0.3);
And I'd like to filter the keys in the same way I can simply filter the values. So if I filter the values on "return the elements that are bigger than 0.2" would be
print_r(array_filter($myArray,"biggerThanFilter");
with
function biggerThanFilter($v){
return $v>0.2;
}
But how would I apply a filter to the keys which say the "keyValueIsBiggerThan50"
i.e something like this
print_r(array_KEY_filter($myArray,"keyValueIsBiggerThan50");
function keyValueIsBiggerThan50($key){
return $key*1>50;
}
I would loop through the array_keys and unset, personally:
function array_filter_keys($array, callable $fn)
foreach (array_keys($array) as $key) {
if (!$fn($key)) unset($array[$key])
}
return $array;
}
$filtered_array = array_filter_keys($array, function($key) { return $key > 50 });
This assumes PHP >= 5.4
function keyValueIsBiggerThan50 ($myArray) {
$newArray = array();
foreach($myArray as $key => $value){
if($key * 1 > 50){
$newArray[$key] = $value
}
}
return $newArray;
}
to be used like
print_r(keyValueIsBiggerThan50 ($myArray));
Are you looking for this specific case, or a generic?
For PHP 5.6+ my answer to a similar question also applies to this one: use ARRAY_FILTER_USE_KEY
<?php
$myArray = ['28' => 0.01, '51' => -0.1, '48' => 0.4, '53' => -0.3];
$filtered = array_filter(
$myArray,
function ($key) {
return $key > 50;
},
ARRAY_FILTER_USE_KEY
);
?>
For PHP < 5.6, array_diff_ukey performs a custom comparison of two arrays by key and appears to do a full N x M comparison so you can filter a single array by using a dummy as the second array.
Using PHP 5.5.9, I used the following to remove the numeric key elements from an array:
$res = array_diff_ukey($res, array(0), function ($a,$b){ return is_string($a); });
I'm currently using array_map to apply callbacks to elements of an array. But I want to be able to pass an argument to the callback function like array_walk does.
I suppose I could just use array_walk, but I need the return value to be an array like if you use array_map, not TRUE or FALSE.
So is it possible to use array_map and pass an argument to the callback function? Or perhaps make the array_walk return an array instead of boolean?
You don't need it to return an array.
Instead of:
$newArray = array_function_you_are_looking_for($oldArray, $funcName);
It's:
$newArray = $oldArray;
array_walk($newArray, $funcName);
If you want return value is an array, just use array_map. To add additional parameters to array_map use "use", ex:
array_map(function($v) use ($tmp) { return $v * $tmp; }, $array);
or
array_map(function($v) use ($a, $b) { return $a * $b; }, $array);
Depending on what kind of arguments you need to pass, you could create a wrapped function:
$arr = array(2, 4, 6, 8);
function mapper($val, $constant) {
return $val * $constant;
}
$constant = 3;
function wrapper($val) {
return mapper($val, $GLOBALS['constant']);
}
$arr = array_map('wrapper', $arr);
This actually seems too simple to be true. I suspect we'll need more context to really be able to help.
To expand a bit on Hieu's great answer, you can also use the $key => $value pairs of the original array. Here's an example with some code borrowed from comment section http://php.net/manual/en/function.array-map.php
The following will use 'use' and include an additional parameter which is a new array.
Below code will grab "b_value" and "d_value" and put into a new array $new_arr
(useless example to show a point)
// original array
$arr = ("a" => "b_value",
"c" => "d_value");
// new array
$new_arr = array();
array_map(function($k,$v) use (&$new_arr) { $new_arr[] = $v;}, array_keys($arr), $arr);
^ $k is key and $v is value
print_r of $new_arr is
Array
(
[0] => b_value
[1] => d_value
)
This question is actually inspired from another one here on SO and I wanted to expand it a bit.
Having an associative array in PHP is it possible to sort its values, but where the values are equal to preserve the original key order, using one (or more) of PHP's built in sort function?
Here is a script I used to test possible solutions (haven't found any):
<?php
header('Content-type: text/plain');
for($i=0;$i<10;$i++){
$arr['key-'.$i] = rand(1,5)*10;
}
uasort($arr, function($a, $b){
// sort condition may go here //
// Tried: return ($a == $b)?1:($a - $b); //
// Tried: return $a >= $b; //
});
print_r($arr);
?>
Pitfall: Because the keys are ordered in the original array, please don't be tempted to suggest any sorting by key to restore to the original order. I made the example with them ordered to be easier to visually check their order in the output.
Since PHP does not support stable sort after PHP 4.1.0, you need to write your own function.
This seems to do what you're asking: http://www.php.net/manual/en/function.usort.php#38827
As the manual says, "If two members compare as equal, their order in the sorted array is undefined." This means that the sort used is not "stable" and may change the order of elements that compare equal.
Sometimes you really do need a stable sort. For example, if you sort a list by one field, then sort it again by another field, but don't want to lose the ordering from the previous field. In that case it is better to use usort with a comparison function that takes both fields into account, but if you can't do that then use the function below. It is a merge sort, which is guaranteed O(n*log(n)) complexity, which means it stays reasonably fast even when you use larger lists (unlike bubblesort and insertion sort, which are O(n^2)).
<?php
function mergesort(&$array, $cmp_function = 'strcmp') {
// Arrays of size < 2 require no action.
if (count($array) < 2) return;
// Split the array in half
$halfway = count($array) / 2;
$array1 = array_slice($array, 0, $halfway);
$array2 = array_slice($array, $halfway);
// Recurse to sort the two halves
mergesort($array1, $cmp_function);
mergesort($array2, $cmp_function);
// If all of $array1 is <= all of $array2, just append them.
if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) {
$array = array_merge($array1, $array2);
return;
}
// Merge the two sorted arrays into a single sorted array
$array = array();
$ptr1 = $ptr2 = 0;
while ($ptr1 < count($array1) && $ptr2 < count($array2)) {
if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) {
$array[] = $array1[$ptr1++];
}
else {
$array[] = $array2[$ptr2++];
}
}
// Merge the remainder
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++];
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++];
return;
}
?>
Also, you may find this forum thread interesting.
array_multisort comes in handy, just use an ordered range as second array ($order is just temporary, it serves to order the equivalent items of the first array in its original order):
$a = [
"key-0" => 5,
"key-99" => 3,
"key-2" => 3,
"key-3" => 7
];
$order = range(1,count($a));
array_multisort($a, SORT_ASC, $order, SORT_ASC);
var_dump($a);
Output
array(4) {
["key-99"]=>
int(3)
["key-2"]=>
int(3)
["key-0"]=>
int(5)
["key-3"]=>
int(7)
}
I used test data with not-ordered keys to demonstrate that it works correctly. Nonetheless, here is the output your test script:
Array
(
[key-1] => 10
[key-4] => 10
[key-5] => 20
[key-8] => 20
[key-6] => 30
[key-9] => 30
[key-2] => 40
[key-0] => 50
[key-3] => 50
[key-7] => 50
)
Downside
It only works with predefined comparisons, you cannot use your own comparison function. The possible values (second parameter of array_multisort()) are:
Sorting type flags:
SORT_ASC - sort items ascendingly.
SORT_DESC - sort items descendingly.
SORT_REGULAR - compare items normally (don't change types)
SORT_NUMERIC - compare items numerically
SORT_STRING - compare items as strings
SORT_LOCALE_STRING - compare items as strings, based on the current locale. It uses the locale, which can be changed using
setlocale()
SORT_NATURAL - compare items as strings using "natural ordering" like natsort()
SORT_FLAG_CASE - can be combined (bitwise OR) with SORT_STRING or SORT_NATURAL to sort strings case-insensitively
For completeness sake, you should also check out the Schwartzian transform:
// decorate step
$key = 0;
foreach ($arr as &$item) {
$item = array($item, $key++); // add array index as secondary sort key
}
// sort step
asort($arr); // sort it
// undecorate step
foreach ($arr as &$item) {
$item = $item[0]; // remove decoration from previous step
}
The default sort algorithm of PHP works fine with arrays, because of this:
array(1, 0) < array(2, 0); // true
array(1, 1) < array(1, 2); // true
If you want to use your own sorting criteria you can use uasort() as well:
// each parameter is an array with two elements
// [0] - the original item
// [1] - the array key
function mysort($a, $b)
{
if ($a[0] != $b[0]) {
return $a[0] < $b[0] ? -1 : 1;
} else {
// $a[0] == $b[0], sort on key
return $a[1] < $b[1] ? -1 : 1; // ASC
}
}
This is a solution using which you can achieve stable sort in usort function
public function sortBy(array &$array, $value_compare_func)
{
$index = 0;
foreach ($array as &$item) {
$item = array($index++, $item);
}
$result = usort($array, function($a, $b) use ($value_compare_func) {
$result = call_user_func($value_compare_func, $a[1], $b[1]);
return $result == 0 ? $a[0] - $b[0] : $result;
});
foreach ($array as &$item) {
$item = $item[1];
}
return $result;
}
Just to complete the responses with some very specific case. If the array keys of $array are the default one, then a simple array_values(asort($array)) is sufficient (here for example in ascending order)
As a workaround for stable sort:
<?php
header('Content-type: text/plain');
for ($i = 0;$i < 10;$i++)
{
$arr['key-' . $i] = rand(1, 5) * 10;
}
uksort($arr, function ($a, $b) use ($arr)
{
if ($arr[$a] === $arr[$b]) return array_search($a, array_keys($arr)) - array_search($b, array_keys($arr));
return $arr[$a] - $arr[$b];
});
print_r($arr);
There are 2 arrays, both with the same length and with the same keys:
$a1 = [1=>2000,65=>1354,103=>1787];
$a2 = [1=>'hello',65=>'hi',103=>'goodevening'];
asort($a1);
The keys of a1 and a2 are id's from a database.
a1 gets sorted by value. Once sorted, how can we use the same sorting order in a2?
Thanks!
I believe this works:
$a1 = array(1=>2000,65=>1354,103=>1787);
$a2 = array(1=>'hello',65=>'hi',103=>'goodevening');
asort($a1); // sort $a1, maintaining array index
// sort $a2 by key, using the order of $a1
function my_uksort($a, $b) {
global $a1;
return $a1[$a] < $a1[$b] ? -1 : 1;
}
uksort($a2, 'my_uksort');
var_dump($a1);
var_dump($a2);
Not optimal maybe.. but it's short:
$a1 = array(1=>2000,65=>1354,103=>1787);
$a2 = array(1=>'hello',65=>'hi',103=>'goodevening');
asort($a1);
foreach(array_keys($a1) as $i) $out[$i] = $a2[$i];
echo join("\n", $out);
look into uasort() also
foreach($a1 as $key => $value){
//do something with $a2
echo $a2[$key];
}
You probably want to look at array_multisort() if you can handle losing ID association (arrays will be re-indexed).
array_multisort($a1, $a2);
I need to get the keys from values that are duplicates. I tried to use array_search and that worked fine, BUT I only got the first value as a hit.
I need to get both keys from the duplicate values, in this case 0 and 2. The search result output as an array would be good.
Is there a PHP function to do this or do I need to write some multiple loops to do it?
$list[0][0] = "2009-09-09";
$list[0][1] = "2009-05-05";
$list[0][2] = "2009-09-09";
$list[1][0] = "first-paid";
$list[1][1] = "1";
$list[1][2] = "last-unpaid";
echo array_search("2009-09-09",$list[0]);
You want array_keys with the search value
array_keys($list[0], "2009-09-09");
which will return an array of the keys with the specified value, in your case [0, 2]. If you want to find the duplicates as well, you can first make a pass with array_unique, then iterate over that array using array_keys on the original; anything which returns an array of length > 1 is a duplicate, and the result is the keys in which the duplicates are stored. Something like...
$uniqueKeys = array_unique($list[0])
foreach ($uniqueKeys as $uniqueKey)
{
$v = array_keys($list[0], $uniqueKey);
if (count($v) > 1)
{
foreach ($v as $key)
{
// Work with $list[0][$key]
}
}
}
In array_search() we can read:
If needle is found in haystack more
than once, the first matching key is
returned. To return the keys for all
matching values, use array_keys() with
the optional search_value parameter
instead.
The following combination of function calls will give you all duplicate values:
$a = array(1, 1, 2, 3, 4, 5, 99, 2, 5, 2);
$unique = array_unique($a); // preserves keys
$diffkeys = array_diff_key($a, $unique);
$duplicates = array_unique($diffkeys);
echo 'Duplicates: ' . join(' ', $duplicates) . "\n"; // 1 2 5
You can achieve that using array_search() by using while loop and the following workaround:
while (($key = array_search("2009-09-09", $list[0])) !== FALSE) {
print($key);
unset($list[0][$key]);
}
Source: cue at openxbox at php.net
For one-multidimensional array, you may use the following function to achieve that (as alternative to array_keys()):
function array_isearch($str, $array){
$found = array();
foreach ($array as $k => $v) {
if (strtolower($v) == strtolower($str)) {
$found[] = $k;
}
}
return $found;
}
Source: robertark, php.net
The PHP manual states in the Return Value section of the array_search() function documentation that you can use array_keys() to accomplish this. You just need to provide the second parameter:
$keys = array_keys($list[0], "2009-09-09");
$userdb=Array
(
(0) => Array
(
(uid) => '100',
(name) => 'Sandra Shush',
(url) => 'urlof100'
),
);
$key = array_search(100, array_column($userdb, 'uid'));