I need to sort the following flat, associative array by its keys, but not naturally. I need to sort the keys by a predefined array of values.
$aShips = [
'0_204' => 1,
'0_205' => 2,
'0_206' => 3,
'0_207' => 4
];
My order array looks like this:
$order = ["0_206", "0_205", "0_204", "0_207"];
The desired result:
[
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4
]
I know how to write a custom sorting function, but I don't know how to integrate the order array.
function cmp($a, $b){
if ($a==$b) return 0;
return ($a<$b)?-1:1;
}
uasort($aShips, "cmp");
If you want to sort by keys. Use uksort. Try the code below.
<?php
$aShips = array('0_204' => 1, '0_205' => 2, '0_206' => 3, '0_207' => 4);
uksort($aShips, function($a, $b) {
return $b > $a;
});
According to the Official PHP Docs, you can use uasort() like this (just for demo):
<?php
// Comparison function
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
// Array to be sorted
$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);
print_r($array);
// Sort and print the resulting array
uasort($array, 'cmp');
print_r($array);
?>
You don't need to leverage a sorting algorithm.
Simply flip $order to use its values as keys (maintaining their order), then merge the two arrays to replace the unwanted values of $order with the values from $aShip.
This assumes that $aShip includes all key values represented in $order, of course. If not, array_intersect_key() can be used to filter $order, but this is dwelling on a fringe case not included in the posted question.
Code: (Demo)
var_export(array_replace(array_flip($order), $aShips));
Output:
array (
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4,
)
This also works when you haven't listed every occurring key in $order -- the unlisted keys are "moved to the back". Proven by this Demo.
Using a custom sorting algorithm can be done and there will be a number of ways, but I'll only show one for comparison's sake.
Reverse your order array and use it as a lookup array while asking uksort() to sort in a descending fashion. If an encountered key is not found in the lookup assign it a value of -1 to ensure it moves to the back of the array.
Code: (Demo)
$lookup = array_flip(array_reverse($order));
uksort($aShips, function($a, $b) use ($lookup) {
return ($lookup[$b] ?? -1) <=> ($lookup[$a] ?? -1);
});
var_export($aShips);
If you don't like reversing the lookup and sorting DESC, you can count the order array to determine a fallback value. This alternative script uses arrow function syntax for brevity and to gain direct access to the variables declared outside of the closure. (Demo)
$lookup = array_flip($order);
$default = count($order);
uksort($aShips, fn($a, $b) => ($lookup[$a] ?? $default) <=> ($lookup[$b] ?? $default));
var_export($aShips);
I'm looking for an efficient way of comparing two arrays in PHP. I have a "before" and an "after" array, and I need to get arrays of specific changes. I did manage to get part of the code right (not sure about how effective it is), but I just can't seem to get the last comparison to work.
The first part of every element is essentially an ID, which stays the same even if the second element, essentially a Name, is changed - note Name1-Renamed for example. The ID is the same. Sometimes an element might be removed (see ID 1233, 'Name3-Deleted' is only in the 'before' array) or added (as in the case of ID 1230, 'Name4-New'). Also note that IDs, while unique, are NOT sorted in any particular order.
So, I would need to find the items that have been
- Added (available 'after', but not 'before')
- Removed (available 'before', but not 'after')
- Changed (available in both, as there is an ID match, but the Name has changed)
And I can't for the life of me find an effective way to get the Changed elements (preferably without ifs or extraneous loops).
Also, what do you think? Is array_udiff the fastest/best method for this particular task?
<?php
//'BEFORE' ARRAY
$arr1 = array( array(1231, 'Name1'), array(1232, 'Name2'), array(1233, 'Name3-Deleted') );
//'AFTER' ARRAY
$arr2 = array( array(1231, 'Name1-Renamed'), array(1232, 'Name2'), array(1230, 'Name4-New') );
//'ADDED' ARRAY
$arr3 = array_udiff($arr2, $arr1, create_function(
'$a,$b',
'return $a[0] - $b[0]; ')
);
//'REMOVED' ARRAY
$arr4 = array_udiff($arr1, $arr2, create_function(
'$a,$b',
'return $a[0] - $b[0]; ')
);
//'CHANGED' ARRAY. CAN'T GET THIS TO WORK PROPERLY. EXPECTED RESULT IS AN ARRAY FOR THE RENAMED ITEM.
$arr5 = array_udiff($arr2, $arr1, create_function(
'$a,$b',
'return (strcmp($a[1],$b[1]))*(strcmp($a[0],$b[0])); ')
);
print("Elements Added\n");
print_r($arr3);
print("Elements Removed\n");
print_r($arr4);
print("Elements Renamed\n");
print_r($arr5);
?>
So, that is pretty much it. Does anybody know how to fix this issue? Thanks in advance for all your help!
//'BEFORE' ARRAY
$arr1 = array( array(1231, 'Name1'), array(1232, 'Name2'), array(1233, 'Name3-Deleted') );
//'AFTER' ARRAY
$arr2 = array( array(1231, 'Name1-Renamed'), array(1232, 'Name2'), array(1230, 'Name4-New') );
// Kudos to AbraCadaver for the following:
$arr1 = array_column($arr1, 1, 0);
$arr2 = array_column($arr2, 1, 0);
$added = array_diff_key($arr2, $arr1);
$deleted = array_diff_key($arr1, $arr2);
$modified = array_diff_key(array_diff($arr2, $arr1), $deleted, $added);
Fill results into $added & $changed arrays instead of outputting directly, if you need them for later.
$copyArr2 = $arr2;
foreach ($arr1 as $subArr1) {
$hit = false;
foreach ($copyArr2 as $key=>$subArr2) {
if ($subArr1[0] == $subArr2[0]) {
$hit = true;
if ($subArr1[1] != $subArr2[1]) {
print("Element changed:\n ".print_r($subArr2, true));
}
unset($copyArr2[$key]);
}
}
if (!$hit)
print("Element removed:\n ".print_r($subArr1, true));
}
print("Elements added:\n");
print_r($copyArr2);
Output:
Element changed:
Array
(
[0] => 1231
[1] => Name1-Renamed
)
Element removed:
Array
(
[0] => 1233
[1] => Name3-Deleted
)
Elements added:
Array
(
[2] => Array
(
[0] => 1230
[1] => Name4-New
)
)
Update: Made small fixes.
Say I've got an array
[0]=>test
[2]=>example.
I want o/p as
[0]=>test
[1]=>example
In my first array
[1]=>NULL
I've tried to remove this and reorder so, I used array_filter() to remove the null value.
Now, how do I reorder the array?
If I understand what you need, I think array_values should help (it will return only the values in the array, reindexed from 0):
print_r( array_values($arr) );
Here's an example of this: http://codepad.org/q7dVqyVY
You might want to use merge:
$newArray = array_merge(array(),$oldArray);
$Array = array('0'=>'test,', '2'=>'example');
ksort($Array);
$ArrayTMP = array_values($Array);
or
$Array = array('0'=>'test,', '2'=>'example');
ksort($Array);
$ArrayTMP = array_merge ($Array,array());
Credit goes to: http://www.codingforums.com/archive/index.php/t-17794.html.
<?php
$a = array(0 => 1, 1 => null, 2 => 3, 3 => 0);
$r = array_values(
array_filter($a, function ($elem)
{
if ( ! is_null($elem))
return true;
})
);
// output:
array (
0 => 1,
1 => 3,
2 => 0,
)
?>
NOTE: I am using an anonymous callback function for array_filter. Anonymous callback only works in php 5.3+ and is the appropriate in this case (IMHO). For previous versions of php just define it as normal.
I have an array:
array( 4 => 'apple', 7 => 'orange', 13 => 'plum' )
I would like to get the first element of this array. Expected result: string apple
One requirement: it cannot be done with passing by reference, so array_shift is not a good solution.
How can I do this?
Original answer, but costly (O(n)):
array_shift(array_values($array));
In O(1):
array_pop(array_reverse($array));
Other use cases, etc...
If modifying (in the sense of resetting array pointers) of $array is not a problem, you might use:
reset($array);
This should be theoretically more efficient, if a array "copy" is needed:
array_shift(array_slice($array, 0, 1));
With PHP 5.4+ (but might cause an index error if empty):
array_values($array)[0];
As Mike pointed out (the easiest possible way):
$arr = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
echo reset($arr); // Echoes "apple"
If you want to get the key: (execute it after reset)
echo key($arr); // Echoes "4"
From PHP's documentation:
mixed reset ( array | object &$array );
Description:
reset() rewinds array's internal pointer to the first element and returns the value of the first array element, or FALSE if the array is
empty.
$first_value = reset($array); // First element's value
$first_key = key($array); // First element's key
current($array)
returns the first element of an array, according to the PHP manual.
Every array has an internal pointer to its "current" element, which is initialized to the first element inserted into the array.
So it works until you have re-positioned the array pointer, and otherwise you'll have to use reset() which ll rewind array and ll return first element of array
According to the PHP manual reset.
reset() rewinds array's internal pointer to the first element and returns the value of the first array element.
Examples of current() and reset()
$array = array('step one', 'step two', 'step three', 'step four');
// by default, the pointer is on the first element
echo current($array) . "<br />\n"; // "step one"
//Forward the array pointer and then reset it
// skip two steps
next($array);
next($array);
echo current($array) . "<br />\n"; // "step three"
// reset pointer, start again on step one
echo reset($array) . "<br />\n"; // "step one"
$arr = $array = array( 9 => 'apple', 7 => 'orange', 13 => 'plum' );
echo reset($arr); // echoes 'apple'
If you don't want to lose the current pointer position, just create an alias for the array.
PHP 7.3 added two functions for getting the first and the last key of an array directly without modification of the original array and without creating any temporary objects:
array_key_first
array_key_last
Apart from being semantically meaningful, these functions don't even move the array pointer (as foreach would do).
Having the keys, one can get the values by the keys directly.
Examples (all of them require PHP 7.3+)
Getting the first/last key and value:
$my_array = ['IT', 'rules', 'the', 'world'];
$first_key = array_key_first($my_array);
$first_value = $my_array[$first_key];
$last_key = array_key_last($my_array);
$last_value = $my_array[$last_key];
Getting the first/last value as one-liners, assuming the array cannot be empty:
$first_value = $my_array[ array_key_first($my_array) ];
$last_value = $my_array[ array_key_last($my_array) ];
Getting the first/last value as one-liners, with defaults for empty arrays:
$first_value = empty($my_array) ? 'default' : $my_array[ array_key_first($my_array) ];
$last_value = empty($my_array) ? 'default' : $my_array[ array_key_last($my_array) ];
You can get the Nth element with a language construct, "list":
// First item
list($firstItem) = $yourArray;
// First item from an array that is returned from a function
list($firstItem) = functionThatReturnsArray();
// Second item
list( , $secondItem) = $yourArray;
With the array_keys function you can do the same for keys:
list($firstKey) = array_keys($yourArray);
list(, $secondKey) = array_keys($yourArray);
PHP 5.4+:
array_values($array)[0];
Some arrays don't work with functions like list, reset or current. Maybe they're "faux" arrays - partially implementing ArrayIterator, for example.
If you want to pull the first value regardless of the array, you can short-circuit an iterator:
foreach($array_with_unknown_keys as $value) break;
Your value will then be available in $value and the loop will break after the first iteration. This is more efficient than copying a potentially large array to a function like array_unshift(array_values($arr)).
You can grab the key this way too:
foreach($array_with_unknown_keys as $key=>$value) break;
If you're calling this from a function, simply return early:
function grab_first($arr) {
foreach($arr as $value) return $value;
}
Suppose:
$array = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
Just use:
$array[key($array)]
to get first element or
key($array)
to get first key.
Or you can unlink the first if you want to remove it.
From Laravel's helpers:
function head($array)
{
return reset($array);
}
The array being passed by value to the function, the reset() affects the internal pointer of a copy of the array, and it doesn't touch the original
array (note it returns false if the array is empty).
Usage example:
$data = ['foo', 'bar', 'baz'];
current($data); // foo
next($data); // bar
head($data); // foo
next($data); // baz
Also, here is an alternative. It's very marginally faster, but more interesting. It lets easily change the default value if the array is empty:
function head($array, $default = null)
{
foreach ($array as $item) {
return $item;
}
return $default;
}
For the record, here is another answer of mine, for the array's last element.
Keep this simple! There are lots of correct answers here, but to minimize all the confusion, these two work and reduce a lot of overhead:
key($array) gets the first key of an array
current($array) gets the first value of an array
EDIT:
Regarding the comments below. The following example will output: string(13) "PHP code test"
$array = array
(
'1' => 'PHP code test',
'foo' => 'bar', 5 , 5 => 89009,
'case' => 'Random Stuff: '.rand(100,999),
'PHP Version' => phpversion(),
0 => 'ending text here'
);
var_dump(current($array));
Simply do:
array_shift(array_slice($array,0,1));
I would do echo current($array) .
$arr = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
foreach($arr as $first) break;
echo $first;
Output:
apple
PHP 7.3 added two functions for getting the first and the last key of an array directly without modification of the original array and without creating any temporary objects:
array_key_first
array_key_last
"There are several ways to provide this functionality for versions prior to PHP 7.3.0. It is possible to use array_keys(), but that may be rather inefficient. It is also possible to use reset() and key(), but that may change the internal array pointer. An efficient solution, which does not change the internal array pointer, written as polyfill:"
<?php
if (!function_exists('array_key_first')) {
function array_key_first($arr) {
foreach($arr as $key => $unused) {
return $key;
}
return NULL;
}
}
if (!function_exists('array_key_last')) {
function array_key_last($arr) {
return array_key_first(array_reverse($arr, true));
}
}
?>
$myArray = array (4 => 'apple', 7 => 'orange', 13 => 'plum');
$arrayKeys = array_keys($myArray);
// The first element of your array is:
echo $myArray[$arrayKeys[0]];
$array=array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
$firstValue = each($array)[1];
This is much more efficient than array_values() because the each() function does not copy the entire array.
For more info see http://www.php.net/manual/en/function.each.php
A kludgy way is:
$foo = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
function get_first ($foo) {
foreach ($foo as $k=>$v){
return $v;
}
}
print get_first($foo);
Most of these work! BUT for a quick single line (low resource) call:
$array = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
echo $array[key($array)];
// key($array) -> will return the first key (which is 4 in this example)
Although this works, and decently well, please also see my additional answer:
https://stackoverflow.com/a/48410351/1804013
Use:
$first = array_slice($array, 0, 1);
$val= $first[0];
By default, array_slice does not preserve keys, so we can safely use zero as the index.
This is a little late to the game, but I was presented with a problem where my array contained array elements as children inside it, and thus I couldn't just get a string representation of the first array element. By using PHP's current() function, I managed this:
<?php
$original = array(4 => array('one', 'two'), 7 => array('three', 'four'));
reset($original); // to reset the internal array pointer...
$first_element = current($original); // get the current element...
?>
Thanks to all the current solutions helped me get to this answer, I hope this helps someone sometime!
<?php
$arr = array(3 => "Apple", 5 => "Ball", 11 => "Cat");
echo array_values($arr)[0]; // Outputs: Apple
?>
Other Example:
<?php
$arr = array(3 => "Apple", 5 => "Ball", 11 => "Cat");
echo current($arr); // Outputs: Apple
echo reset($arr); // Outputs: Apple
echo next($arr); // Outputs: Ball
echo current($arr); // Outputs: Ball
echo reset($arr); // Outputs: Apple
?>
I think using array_values would be your best bet here. You could return the value at index zero from the result of that function to get 'apple'.
Two solutions for you.
Solution 1 - Just use the key. You have not said that you can not use it. :)
<?php
// Get the first element of this array.
$array = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
// Gets the first element by key
$result = $array[4];
// Expected result: string apple
assert('$result === "apple" /* Expected result: string apple. */');
?>
Solution 2 - array_flip() + key()
<?php
// Get first element of this array. Expected result: string apple
$array = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
// Turn values to keys
$array = array_flip($array);
// You might thrown a reset in just to make sure
// that the array pointer is at the first element.
// Also, reset returns the first element.
// reset($myArray);
// Return the first key
$firstKey = key($array);
assert('$firstKey === "apple" /* Expected result: string apple. */');
?>
Solution 3 - array_keys()
echo $array[array_keys($array)[0]];
No one has suggested using the ArrayIterator class:
$array = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
$first_element = (new ArrayIterator($array))->current();
echo $first_element; //'apple'
gets around the by reference stipulation of the OP.
I imagine the author just was looking for a way to get the first element of an array after getting it from some function (mysql_fetch_row, for example) without generating a STRICT "Only variables should be passed by reference".
If it so, almost all the ways described here will get this message... and some of them uses a lot of additional memory duplicating an array (or some part of it). An easy way to avoid it is just assigning the value inline before calling any of those functions:
$first_item_of_array = current($tmp_arr = mysql_fetch_row(...));
// or
$first_item_of_array = reset($tmp_arr = func_get_my_huge_array());
This way you don't get the STRICT message on screen, nor in logs, and you don't create any additional arrays. It works with both indexed AND associative arrays.
Use array_keys() to access the keys of your associative array as a numerical indexed array, which is then again can be used as key for the array.
When the solution is arr[0]:
(Note, that since the array with the keys is 0-based index, the 1st
element is index 0)
You can use a variable and then subtract one, to get your logic, that 1 => 'apple'.
$i = 1;
$arr = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
echo $arr[array_keys($arr)[$i-1]];
Output:
apple
Well, for simplicity- just use:
$arr = array( 4 => 'apple', 7 => 'orange', 13 => 'plum' );
echo $arr[array_keys($arr)[0]];
Output:
apple
By the first method not just the first element, but can treat an associative array like an indexed array.
I don't like fiddling with the array's internal pointer, but it's also inefficient to build a second array with array_keys() or array_values(), so I usually define this:
function array_first(array $f) {
foreach ($f as $v) {
return $v;
}
throw new Exception('array was empty');
}
This is not so simple response in the real world. Suppose that we have these examples of possible responses that you can find in some libraries.
$array1 = array();
$array2 = array(1,2,3,4);
$array3 = array('hello'=>'world', 'foo'=>'bar');
$array4 = null;
var_dump('reset1', reset($array1));
var_dump('reset2', reset($array2));
var_dump('reset3', reset($array3));
var_dump('reset4', reset($array4)); // Warning
var_dump('array_shift1', array_shift($array1));
var_dump('array_shift2', array_shift($array2));
var_dump('array_shift3', array_shift($array3));
var_dump('array_shift4', array_shift($array4)); // Warning
var_dump('each1', each($array1));
var_dump('each2', each($array2));
var_dump('each3', each($array3));
var_dump('each4', each($array4)); // Warning
var_dump('array_values1', array_values($array1)[0]); // Notice
var_dump('array_values2', array_values($array2)[0]);
var_dump('array_values3', array_values($array3)[0]);
var_dump('array_values4', array_values($array4)[0]); // Warning
var_dump('array_slice1', array_slice($array1, 0, 1));
var_dump('array_slice2', array_slice($array2, 0, 1));
var_dump('array_slice3', array_slice($array3, 0, 1));
var_dump('array_slice4', array_slice($array4, 0, 1)); // Warning
list($elm) = $array1; // Notice
var_dump($elm);
list($elm) = $array2;
var_dump($elm);
list($elm) = $array3; // Notice
var_dump($elm);
list($elm) = $array4;
var_dump($elm);
Like you can see, we have several 'one line' solutions that work well in some cases, but not in all.
In my opinion, you have should that handler only with arrays.
Now talking about performance, assuming that we have always array, like this:
$elm = empty($array) ? null : ...($array);
...you would use without errors:
$array[count($array)-1];
array_shift
reset
array_values
array_slice
array_shift is faster than reset, that is more fast than [count()-1], and these three are faster than array_values and array_slice.