I have the following two associative arrays:
$arr1 = array(
'id' => 1,
'text' => 'Some text is here',
'timestamp' => 130458750
)
$arr2 = array(
'post_id' => 12,
'content' => 'Some content is here too',
'created_at' => 1402154823
)
I want to sort these two arrays based on timestamp and created_at keys, i.e. the larger integer is first and lesser second and so on. Is that possible using PHP's built-in functions? If not, how may I approach the problem?
EDIT
The desired result is: Here, $arr1's timestamp is less and $arr2's timestamp (i.e. created_at) is larger. So, I want to get a combination of $arr1 and $arr2 where $arr2 is first and $arr1 is second. Something like:
$sorted_arr = array($arr2, $arr1);
First let me say that one of your array contains timestamp and second contains created_at. I assumed both of them should be created_at.
In case you want to "sort" just two entries like you said in the comments, the task is straightforward:
<?php
$arr1 = array(
'id' => 1,
'text' => 'Some text is here',
'created_at' => 130458750 #corrected from "timestamp"
);
$arr2 = array(
'post_id' => 12,
'content' => 'Some content is here too',
'created_at' => 1402154823
);
$posts = $arr2['created_at'] > $arr1['created_at']
? [$arr2, $arr1]
: [$arr1, $arr2];
But apparently what you're after is a way to sort the posts if they're in array of unknown length. In that case you should use uasort built-in PHP function, which allows to sort by user-defined function and maintains indexes in associative arrays (as opposed to plain usort). Example code would then look like this:
$posts = [$arr1, $arr2];
uasort($posts, function($a, $b)
{
return $b['created_at'] - $a['created_at'];
});
var_dump($posts);
which outputs:
array(2) {
[1]=>
array(3) {
["post_id"]=>
int(12)
["content"]=>
string(24) "Some content is here too"
["created_at"]=>
int(1402154823)
}
[0]=>
array(3) {
["id"]=>
int(1)
["text"]=>
string(17) "Some text is here"
["created_at"]=>
int(130458750)
}
}
To get reverse order you may just reverse arguments in custom sort function, i.e. swap $a with $b.
Combining rr-'s solution, I came up with the following:
$arr1 = array(
'id' => 1,
'text' => 'Some text is here',
'timestamp' => 130458750
);
$arr2 = array(
'post_id' => 12,
'content' => 'Some content is here too',
'created_at' => 1402154823
);
$arr3 = array(
'post_id' => 21,
'content' => 'Some content is here too',
'created_at' => 1258475
);
$arr = [];
$arr[] = $arr1;
$arr[] = $arr2;
$arr[] = $arr3;
uasort($arr, function($a, $b)
{
$t1 = isset($a['timestamp']) ? $a['timestamp'] : $a['created_at'];
$t2 = isset($b['timestamp']) ? $b['timestamp'] : $b['created_at'];
return $t2 - $t1
});
var_dump($arr);
It sorts the arrays even when the keys are different.
Related
Is there a PHP function I've missed that will change the keys of the parent array when given the key name of its child(associative array) or is there at least an alternative to a foreach loop which i am using at the moment to change the keys.
Example array
$arr = array(
array(
'id' => 1,
'name' => 'one',
),
array(
'id' => 2,
'name' => 'two',
),
array(
'id' => 3,
'name' => 'three',
)
);
I would like it to work like so..
$arr_name = array_change_key($arr,'name');
print_r($arr_name);
$arr_name => array(
'one', => array(
'id' => 1,
'name' => 'one',
),
'two' => array(
'id' => 2,
'name' => 'two',
),
'three' => array(
'id' => 3,
'name' => 'three',
)
);
//$arr is unchanged
This is just an added extra (not sure if possible)
array_change_key($arr,'name');
print_r($arr);
//$arr has changed because it doesn't have a variable to set
$arr => array(
'one', => array(
'id' => 1,
'name' => 'one',
),
'two' => array(
'id' => 2,
'name' => 'two',
),
'three' => array(
'id' => 3,
'name' => 'three',
)
);
print_r($arr[0]); //undefined index
If I understand the question correctly, something like:
$arr = array_combine(
array_column($arr, 'name'),
$arr
);
will use the name value from each record as the parent key, and give
array(3) {
["one"]=>
array(2) {
["id"]=>
int(1)
["name"]=>
string(3) "one"
}
["two"]=>
array(2) {
["id"]=>
int(2)
["name"]=>
string(3) "two"
}
["three"]=>
array(2) {
["id"]=>
int(3)
["name"]=>
string(5) "three"
}
}
You would have to tell the function whether or not to "pass by reference" it has no way of knowing whether you are trying to set the returned result to a variable;
function array_change_key(array &$array, $key, $pass_by_reference = false){
if($pass_by_reference){
// check is_scalar($key)
if(!is_scalar($key)) return FALSE;
// we already know isset($array), is_array($array) and isset(key) are true because $pass_by_reference is true;
$array = markBakersAnswer($array,$key);
return TRUE;
// pass-by-reference functions usually return true or false
}
return markBakersAnswer($array,$key);
}
MarkBakersAnswer +1
$new_array = array_change_key($arr, 'name'); // $arr unchanged and $new_array == array with new keys
$new_array = array_change_key($arr, 'name', false); // $arr unchanged and $new_array == array with new keys
$new_array = array_change_key($arr, 'name', true); // $arr changed (new keys), $new_array = TRUE;
$new_array = array_change_key($arr, array(), true); // $arr changed (new keys), $new_array = FALSE;
This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed 5 months ago.
I have an array with every containing value being another array with one value. I was looking for a way to flatten the array and succeeded but somehow I am having this feeling that it can be done better. Is this the best way or can it still be improved?
<?php
$array = array(
'1' => array('id' => '123'),
'2' => array('id' => '22'),
'3' => array('id' => '133'),
'4' => array('id' => '143'),
'5' => array('id' => '153'),
);
array_walk_recursive($array, function($v, $k) use (&$result) {
$result[] = $v;
});
You can achieve that using the array_map function:
$func = function($value) {
return $value['id'];
};
$array2 = array_map($func, $array);
Or if you want to keep it in one line do:
$array2 = array_map(function($value) { return $value['id']; }, $array);
This will return the array flattened and keeps your initial keys:
array(5) {
[1]=>
string(3) "123"
[2]=>
string(2) "22"
[3]=>
string(3) "133"
[4]=>
string(3) "143"
[5]=>
string(3) "153"
}
If you don't want to keep the keys, then call the following at the end:
$array2 = array_values($array2);
You can use array_map() passing null as the first argument while unpacking the passed array with the splat operator and grab the first element of the result:
$result = array_map(null, ...$array)[0];
Another option would be to use array_column() which creates an array from a single column of a given multidimensional array - feed it your array and column key, e.g.:
$result = array_column($array, 'id');
Output for either:
print_r($result);
Array
(
[0] => 123
[1] => 22
[2] => 133
[3] => 143
[4] => 153
)
This is what I would do. Its cleaner:
$array = array(
'1' => array('id' => '123'),
'2' => array('id' => '22'),
'3' => array('id' => '133'),
'4' => array('id' => '143'),
'5' => array('id' => '153'),
);
foreach($array as $key => $arr){
$result[] = $arr['id'];
}
If the depth won't ever change a foreach loop would likely be faster. That anonymous function has some overhead to it and it really shows the longer your array gets.
If the depth is variable as well, however, then this is the fastest way to traverse and flatten.
How can assign the values from one array to another array? For example:
//array with empty value
$targetArray = array(
'a' => '',
'b' => '',
'c' => '',
'd' => ''
);
// array with non-empty values, but might be missing keys from the target array
$sourceArray = array(
'a'=>'a',
'c'=>'c',
'd'=>'d'
);
The result I would like to see is the following:
$resultArray = array(
'a'=>'a',
'b'=>'',
'c'=>'c',
'd'=>'d'
);
I think the function you are looking for is array_merge.
$resultArray = array_merge($targetArray,$sourceArray);
Use array_merge:
$merged = array_merge($targetArray, $sourceArray);
// will result array('a'=>'a','b'=>'','c'=>'c','d'=>'d');
Use array_merge():
$targetArray = array('a'=>'','b'=>'','c'=>'','d'=>'');
$sourceArray = array('a'=>'a','c'=>'c','d'=>'d');
$result = array_merge( $targetArray, $sourceArray);
This outputs:
array(4) {
["a"]=>
string(1) "a"
["b"]=>
string(0) ""
["c"]=>
string(1) "c"
["d"]=>
string(1) "d"
}
Perhaps a more intuitive/indicative function for this task is array_replace(). It performs identically to array_merge() on associative arrays. (Demo)
var_export(
array_replace($targetArray, $sourceArray)
);
Output:
array (
'a' => 'a',
'b' => '',
'c' => 'c',
'd' => 'd',
)
A similar but not identical result can be achieved with the union operator, but notice that its input parameters are in reverse order and the output array has keys from $targetArray then keys from $sourceArray.
var_export($sourceArray + $targetArray);
Output:
array (
'a' => 'a',
'c' => 'c',
'd' => 'd',
'b' => '',
)
I'm trying to sort a multi-dimensional array by another array, but have so far come up short.
array_multisort seems be working only for real sorting.
Suppose I have these 2 arrays:
$order = array(2,3,1);
$data = array(
array('id' => 1, 'title' => 'whatever'),
array('id' => 2, 'title' => 'whatever'),
array('id' => 3, 'title' => 'whatever')
);
Now I would like to sort my $data array according to the order in my $order array.
This is what I would like the result to be:
$data = array(
array('id' => 2, 'title' => 'whatever'),
array('id' => 3, 'title' => 'whatever')
array('id' => 1, 'title' => 'whatever'),
);
I can accomplish this easily by running a nested loop, but that would not scale well (my array is pretty big, and the arrays have many more fields).
In your example the ids in the $data array are are numbered consecutively and starting at 1. The code I give below assumes this is always the case. If this is not the case, the code does not work.
$result = array();
$index = 0;
foreach ($order as $position) {
$result[$index] = $data[$position - 1];
$index++;
}
At http://codepad.org/YC8w0yHh you can see that it works for your example data.
EDIT
If the assumption mentioned above does not hold, the following code will achieve the same result:
<?php
$data = array(
array('id' => 1, 'title' => 'whatever'),
array('id' => 2, 'title' => 'whatever'),
array('id' => 3, 'title' => 'whatever')
);
$order = array(2,3,1);
$order = array_flip($order);
function cmp($a, $b)
{
global $order;
$posA = $order[$a['id']];
$posB = $order[$b['id']];
if ($posA == $posB) {
return 0;
}
return ($posA < $posB) ? -1 : 1;
}
usort($data, 'cmp');
var_dump($data);
See http://codepad.org/Q7EcTSfs for proof.
By calling array_flip() on the $order array it can be used for position lookup. This is like a hashtable lookup, which is linear in time, or O(n). You cannot do better.
There is no built-in function for this in PHP and i am unable to think of any custom function, which would do this using usort. But array_map is simple enough, imo, so why not use it instead?
$sorted = array_map(function($v) use ($data) {
return $data[$v - 1];
}, $order);
For those of you who want to sort data based on an array with actual IDs, rather than based on an array with indexes like in the accepted answer - you can use the following simple comparison function for the usort:
usort($data, function($a, $b) use ($order) {
$posA = array_search($a['id'], $order);
$posB = array_search($b['id'], $order);
return $posA - $posB;
});
So the following example will work fine and you won't get the Undefined offset notices and an array with null values:
$order = [20, 30, 10];
$data = [
['id' => 10, 'title' => 'Title 1'],
['id' => 20, 'title' => 'Title 2'],
['id' => 30, 'title' => 'Title 3']
];
usort($data, function($a, $b) use ($order) {
$posA = array_search($a['id'], $order);
$posB = array_search($b['id'], $order);
return $posA - $posB;
});
echo '<pre>', var_dump($data), '</pre>';
Output:
array(3) {
[0]=>
array(2) {
["id"]=>
int(20)
["title"]=>
string(7) "Title 2"
}
[1]=>
array(2) {
["id"]=>
int(30)
["title"]=>
string(7) "Title 3"
}
[2]=>
array(2) {
["id"]=>
int(10)
["title"]=>
string(7) "Title 1"
}
}
This would be how I would do. I would use a custom usort function (arr_sort) in conjunction with the $data array.
<?php
$order = array(2,3,1);
$data = array(
array('id' => 1, 'title' => 'whatever'),
array('id' => 2, 'title' => 'whatever'),
array('id' => 3, 'title' => 'whatever')
);
function arr_sort($a,$b){
global $order;
foreach ($order as $key => $value) {
if ($value==$a['id']) {
return 0;
break;
}
if ($value==$b['id']) {
return 1;
break;
}
}
}
usort($data,'arr_sort');
echo "<pre>";
print_r($data);
echo "<pre>";
You could try using a custom sort with usort(). This way you can use the first array to determine the order of the second array.
$arr = array(
'a1'=>'1',
'a2'=>'2'
);
I need to move the a2 to the top, as well as keep the a2 as a key how would I go on about it I can't seem to think a way without messing something up :)
Here is a solution which works correctly both with numeric and string keys:
function move_to_top(&$array, $key) {
$temp = array($key => $array[$key]);
unset($array[$key]);
$array = $temp + $array;
}
It works because arrays in PHP are ordered maps.
Btw, to move an item to bottom use:
function move_to_bottom(&$array, $key) {
$value = $array[$key];
unset($array[$key]);
$array[$key] = $value;
}
You can achieve that this way:
$arr = array(
'a1'=>'1',
'a2'=>'2'
);
end($arr);
$last_key = key($arr);
$last_value = array_pop($arr);
$arr = array_merge(array($last_key => $last_value), $arr);
/*
print_r($arr);
will output (this is tested):
Array ( [a2] => 2 [a1] => 1 )
*/
try this:
$key = 'a3';
$arr = [
'a1' => '1',
'a2' => '2',
'a3' => '3',
'a4' => '4',
'a5' => '5',
'a6' => '6'
];
if (isset($arr[ $key ]))
$arr = [ $key => $arr[ $key ] ] + $arr;
result:
array(
'a3' => '3',
'a1' => '1',
'a2' => '2',
'a4' => '4',
'a5' => '5',
'a6' => '6'
)
Here's a simple one liner to get this done with array_splice() and the union operator:
$arr = array('a1'=>'1', 'a2'=>'2', 'a3' => '3');
$arr = array_splice($arr,array_search('a2',array_keys($arr)),1) + $arr;
Edit:
In retrospect I'm not sure why I wouldn't just do this:
$arr = array('a2' => $arr['a2']) + $arr;
Cleaner, easier and probably faster.
You can also look at array_multisort This lets you use one array to sort another. This could, for example, allow you to externalize a hard-coded ordering of values into a config file.
<?php
$ar1 = array(10, 100, 100, 0);
$ar2 = array(1, 3, 2, 4);
array_multisort($ar1, $ar2);
var_dump($ar1);
var_dump($ar2);
?>
In this example, after sorting, the first array will contain 0, 10, 100, 100. The second array will contain 4, 1, 2, 3. The entries in the second array corresponding to the identical entries in the first array (100 and 100) were sorted as well.
Outputs:
array(4) {
[0]=> int(0)
[1]=> int(10)
[2]=> int(100)
[3]=> int(100)
}
array(4) {
[0]=> int(4)
[1]=> int(1)
[2]=> int(2)
[3]=> int(3)
}