PHP bug in array_udiff()? - php

function value_compare_func($a, $b){
if ($a === 'n_3') {
return 0;
}
return 1;
}
$array1 = array("n_1", "n_2", "n_3", "n_4" );
$array2 = array("green");
$result = array_udiff($array1, $array2, "value_compare_func");
print_r($result);
The expected output is:
Array([0] => 'n_1', [1] => 'n_2' , [3] => 'n_4' )
But PHP outputs:
Array([1] => 'n_2' , [3] => 'n_4' )
Where is n_1?

This is not a bug since you are not using the function as described in the docs.
The compare callback MUST compare $a and $b and decide if they are equals, to calculate the difference. The docs also states that you MUST return -1 and 1 to hint whether $a comes before $b; this may sound useless, but is probably used internally.
Your callback translate to something like: "every element comes after every other element, except when the first element is equal to 'n_3' in which case is equal to every other element". Well, it doesn't make sense, just like the result you get.
If you want to remove all element equals to 'n_3', just use array_filter. If you want to compare an array difference, then define a compare callback.

Related

Find the most recent date from a nested array

I have a Multilevel Array (See array structure picture below) and I need to get the sub-nested array with the higher date value.
I was wondering if there is a straight forward way to sort sub-nested arrays by date value or get the highest date value?
Array Map
To do that, usort() function will be useful:
usort($rgData, function($rgX, $rgY)
{
$x = strtotime($rgX['date']);
$y = strtotime($rgY['date']);
return $x<$y?-1:$x!=$y;
});
//var_dump($rgData);
if you want to get highest value, then it will be ['date'] key of last element after doing the sort above.
Edit: if you're sure that format will be exactly same as on picture always, you can use direct string comparison via strcmp (that would be probably faster)
How about using usort():
$input = array(
array('date' => '2013-09-11 13:08:40 +0000'),
array('date' => '2013-09-11 13:09:17 +0000'));
usort($input, function(array $a, array $b) {
$aTimestamp = strtotime($a['date']);
$bTimestamp = strtotime($b['date']);
if($aTimestamp == $bTimestamp) return 0;
return $aTimestamp < $bTimestamp;
});
print_r($input); //$input[0] has the latest date value

How do I remove a specific key in an array using php?

I have an array with 4 values. I would like to remove the value at the 2nd position and then have the rest of the key's shift down one.
$b = array(123,456,789,123);
Before Removing the Key at the 2nd position:
Array ( [0] => 123 [1] => 456 [2] => 789 [3] => 123 )
After I would like the remaining keys to shift down one to fill in the space of the missing key
Array ( [0] => 123 [1] => 789 [2] => 123 )
I tried using unset() on the specific key, but it would not shift down the remaining keys. How do I remove a specific key in an array using php?
You need array_values($b) in order to re-key the array so the keys are sequential and numeric (starting at 0).
The following should do the trick:
$b = array(123,456,789,123);
unset($b[1]);
$b = array_values($b);
echo "<pre>"; print_r($b);
It is represented that your input data is an indexed array (there are no gaps in the sequence of the integer keys which start from zero). I'll compare the obvious techniques that directly deliver the desired result from the OP's sample data.
1. unset() then array_values()
unset($b[1]);
$b = array_value($b);
This is safe to use without checking for the existence of the index -- if missing, there will be no error.
unset() can receive multiple parameters, so if more elements need to be removed, then the number of function calls remains the same. e.g. unset($b[1], $b[3], $b[5]);
unset() cannot be nested inside of array_values() to form a one-liner because unset() modifies the variable and returns no value.
AFAIK, unset() is not particularly handy for removing elements using a dynamic whitelist/blacklist of keys.
2. array_splice()
array_splice($b, 1, 1);
// (input array, starting position, number of elements to remove)
This function is key-ignorant, it will target elements based on their position in the array. This is safe to use without checking for the existence of the position -- if missing, there will be no error.
array_splice() can remove a single element or, at best, remove multiple consecutive elements. If you need to remove non-consecutive elements you would need to make additional function calls.
array_splice() does not require an array_values() call because "Numerical keys in input are not preserved" -- this may or may not be desirable in certain situations.
3. array_filter() nested in array_values()
array_values(
array_filter(
$b,
function($k) {
return $k != 1;
},
ARRAY_FILTER_USE_KEY
)
)
This technique relies on a custom function call and a flag to tell the filter to iterate only the keys.
It will be a relatively poor performer because it will iterate all of the elements regardless of the logical necessity.
It is the most verbose of the options that I will discuss.
It further loses efficiency if you want to employ an in_array() call with a whitelist/blacklist of keys in the custom function.
Prior to PHP7.4, passing a whitelist/blacklist/variable into the custom function scope will require the use of use().
It can be written as a one-liner.
This is safe to use without checking for the existence of the index(es) -- if missing, there will be no error.
4. array_diff_key() nested in array_values()
array_values(
array_diff_key(
$b,
[1 => '']
)
);
This technique isn't terribly verbose, but it is a bit of an overkill if you only need to remove one element.
array_diff_key() really shines when there is a whitelist/blacklist array of keys (which may have a varying element count). PHP is very swift at processing keys, so this function is very efficient at the task that it was designed to do.
The values in the array which is declared as the second parameter of array_diff_key() are completely irrelevant -- they can be null or 999 or 'eleventeen' -- only the keys are respected.
array_diff_key() does not have any scoping challenges, compared to array_filter(), because there is no custom function called.
It can be written as a one-liner.
This is safe to use without checking for the existence of the index(es) -- if missing, there will be no error.
Use array_splice().
array_splice( $b, 1, 1 );
// $b == Array ( [0] => 123 [1] => 789 [2] => 123 )
No one has mentioned this, so i will do: sort() is your friend.
$fruits = array("lemon", "orange", "banana", "apple");
sort($fruits);
foreach($fruits as $key => $val)
echo "fruits[$key] = $val";
output:
fruits[0] = apple
fruits[1] = banana
fruits[2] = lemon
fruits[3] = orange
// remove Lemon, too bitter
unset($fruits[2]);
// keep keys with asort
asort($fruits);
foreach($fruits as $key => $val)
echo "fruits[$key] = $val";
Output:
fruits[0] = apple
fruits[1] = banana
fruits[3] = orange
This is the one you want to use to reindex the keys:
// reindex keys with sort
sort($fruits);
foreach($fruits as $key => $val)
echo "fruits[$key] = $val";
Output:
fruits[0] = apple
fruits[1] = banana
fruits[2] = orange
If you want to remove an item from an array at a specific position, you can obtain the key for that position and then unset it:
$b = array(123,456,789,123);
$p = 2;
$a = array_keys($b);
if ($p < 0 || $p >= count($a))
{
throw new RuntimeException(sprintf('Position %d does not exists.', $p));
}
$k = $a[$p-1];
unset($b[$k]);
This works with any PHP array, regardless where the indexing starts or if strings are used for keys.
If you want to renumber the remaining array just use array_values:
$b = array_values($b);
Which will give you a zero-based, numerically indexed array.
If the original array is a zero-based, numerically indexed array as well (as in your question), you can skip the part about obtaining the key:
$b = array(123,456,789,123);
$p = 2;
if ($p < 0 || $p >= count($b))
{
throw new RuntimeException(sprintf('Position %d does not exists.', $p));
}
unset($b[$p-1]);
$b = array_values($b);
Or directly use array_splice which deals with offsets instead of keys and re-indexes the array (numeric keys in input are not preserved):
$b = array(123,456,789,123);
$p = 2;
if ($p < 0 || $p >= count($b))
{
throw new RuntimeException(sprintf('Position %d does not exists.', $p));
}
array_splice($b, $p-1, 1);

How to count non-empty entries in a PHP array?

Consider:
[name] => Array ( [1] => name#1
[2] => name#2
[3] => name#3
[4] => name#4
[5] =>
[6] =>
[7] =>
[8] =>
[9] =>
)
$name = $_POST['name']
I want the result to be 4.
count ($name) = 9
count (isset($name)) = 1
count (!empty($name)) = 1
I would think that last one would accomplish what I need, but it is not (the empty entries are from unfilled inputs on the form).
You can use array_filter to only keep the values that are “truthy” in the array, like this:
array_filter($array);
If you explicitly want only non-empty, or if your filter function is more complex:
array_filter($array, function($x) { return !empty($x); });
# function(){} only works in in php >5.3, otherwise use create_function
So, to count only non-empty items, the same way as if you called empty(item) on each of them:
count(array_filter($array, function($x) { return !empty($x); }));
However, as empty() is only a shorthand for 'variable is set and the value is truthy', you don't really gain anything from that, because if your array contains values such as FALSE or "0", those would count as empty as well, same as with the plain call to array_filter.
The better way is to be explicit about what you want to count, e.g. if you want to exclude empty strings only, then use:
array_filter($array, function($x) { return ($x !== ""); });
count(array_filter($name));
Possible Solution: First you need to remove empty/null, false and zero values from an array and then count remaining values of an array
If you no need to remove zero values from an array, but remove null and false values
count(array_filter($arrayName, 'strlen'));
//"strlen" use as second parameter if you no need to remove zero '0' values
if you need to remove zero, null and false values from an array
count(array_filter($arrayName));
Here's a simple calculation function:
function non_empty(array $a) {
return array_sum(array_map(function($b) {return empty($b) ? 0 : 1;}, $a));
}
This will preserve array indexes if your form handling function needs them, like when you're associating the third input on name to the third value of another input set, and there are empty inputs in between them.
The easyest aproach is to use count() and array_filter() functions (eventually with an additional callback within array_filter() when we need type compatibility checking, but when the result of default empty() function is enough for us we don't need it).
However, we can also achieve that by using the array_reduce() function, and this approach is slightly more optimal in terms of computational complexity:
$initial = 0;
$count = array_reduce($arr, function($carry, $item) {
$carry += empty($item) ? 0 : 1;
return $carry;
}, $initial);
The $count variable counts all non-empty items in the array $arr.
Note that, the condition can be changed if someone wishes, e.g. instead of empty($item) we can use a stronger condition $item === '', etc.
When the array $arr is empty, $initial is returned.

Return a single Element from an Associative Array from a Function

Lets assume we have two PHP-Arrays:
$some_array = array('a','b','c','d','e','f');
$another_array = array(101,102,103,104,105,106);
In PHP, there are already some Array-Functions that allow the construction of an Associative Array (AKA hash), e,g,;
$hash = array_combine(
$some_array,
$another_array
);
And here it comes. What should I do if I want to create a hash in a more functional style, if I want to compute key and value on the fly and build the hash through a map-operation, like (not working):
# wishful thinking
$hash = array_map(
function($a, $b){ return ($a => $b); },
$some_array,
$another_array
);
The problem here seems to be the expectation, the line
...
function($a, $b){ return ($a => $b); }
...
would indeed return an key value/pair of a hash - which it doesn't.
Q: How can I return a key/value pair from a function - which can be used to build up an associative array?
Addendum
To make clear what I really was looking for, I'll provide a perl example of hash generation:
...
# we have one array of characters (on which our hash generation is based)
my #array = qw{ a b c d e f };
# now *generate* a hash with array contents as keys and
# ascii numbers as values in a *single operation *
# (in Perl, the $_ variable represents the actual array element)
my %hash = map +($_ => ord $_), #array;
...
Result (%hash):
a => 97
b => 98
c => 99
d => 100
e => 101
f => 102
From the responses, I'd now think this is impossible in PHP. Thanks to all respondends.
EDIT: It's not entirely clear whether you're having a problem merely with returning multiple variables from a function, or whether you're having problems storing a function in an array. Your post gives the impression that storing the function in the array works, so I'll tackle the return-multiple-variables problem.
There is no way to return a single instance of a key/value pair in PHP. You have to have them in an array... but remember that in PHP, an array and hashmap are exactly the same thing. It's weird (and controversial), but that means it's perfectly legitimate to return an array/hashmap with the multiple values you wish to return.
There are only two sane ways that I know (from 10+ years of PHP experience) to get more than one variable out of a function. One is the good'ol fashion way of making the input variable changeable.
function vacuumPackSandwitch(&$a, &$b) {
$a = 505;
$b = 707;
}
This will change both $a and $b as opposed to changing copies of them like usual. For example:
$a = 1;
$b = 2;
vacuumPackSandwitch($a, $b);
print $a.' '.$b;
This will return "505 707", not "1 2" like normally. You might have to do:
vacuumPackSandwitch(&$a, &$b);
But if that's the case, the PHP compiler will duly let you know.
The other way is to return an array, which I suppose is the clearer and preferred way.
function ($a, $b) {
return array($a, $b);
}
You can grab both variables at the same time by doing:
list($c, $d) = vacuumPackSandwitch($a, $b);
Hope it helps!
In PHP arrays can be associative too! An int-indexed array is just an associative array with 0 => element0 and so on. Thus, you can accomplish what you are looking for like this:
$somearray = array('name' => 'test', 'pass' => '***', 'group' => 'admin');
function myfunc($a, $b) { return array($a => $b); }
if($somearray['name'] == 'test') { echo 'it works!'; }
// Let's add more data with the function now
$somearray += myfunc('location', 'internet');
//Test the result
if($somearray['location'] == 'internet') { echo 'it works again!'; }
It is really very simple. Hope this helps.
I know that this is an old question, but google still likes it for a search I recently made, so I'll post my findings. There are two ways to do this that come close to what you're attempting, both relying on the same general idea.
Idea 1:
Instead of returning a key => value pair, we return an array with only one element, 'key => value', for each sequential element of the original arrays. Then, we reduce these arrays, merging at every step.
$array = array_map(
function($a, $b){
return array($a => $b);
},
$arr1,
$arr2
);
$array = array_reduce(
$array,
function($carry, $element){
$carry = array_merge($carry, $element);
return $carry;
},
array()
);
OR
Idea 2:
Similar to idea one, but we do the key => value assignment in array_reduce. We pass NULL to array_map, which creates an array of arrays (http://php.net/manual/en/function.array-map.php)
$array = array_map(NULL, $a, $b);
$array = array_reduce(
$array,
function($carry, $element){
$carry[$element[0]] = $element[1];
return $carry;
},
array()
);
Personally, I find Idea 2 to be a lot more elegant than Idea 1, though it requires knowing that passing NULL as the function to array_map creates an array of arrays and is therefore somewhat un-intuitive. I just think of it as a precursor to array_reduce, where all the business happens.
Idea 3:
$carry = array();
$uselessArray = array_map(
function($a, $b) use ($carry){
$carry[$a] = $b;
},
$a,
$b
);
Idea 3 is an alternative to Idea 2, but I think it's hackier than Idea 2. We have to use 'use' to jump out of the function's scope, which is pretty ugly and probably contrary to the functional style OP was seeking.
Lets just streamline Idea 2 a little and see how that looks:
Idea 2(b):
$array = array_reduce(
array_map(NULL, $a, $b),
function($carry, $element){
$carry[$element[0]] = $element[1];
return $carry;
},
array()
);
Yeah, that's nice.

How do I move an array element with a known key to the end of an array in PHP?

Having a brain freeze over a fairly trivial problem. If I start with an array like this:
$my_array = array(
'monkey' => array(...),
'giraffe' => array(...),
'lion' => array(...)
);
...and new elements might get added with different keys but always an array value. Now I can be sure the first element is always going to have the key 'monkey' but I can't be sure of any of the other keys.
When I've finished filling the array I want to move the known element 'monkey' to the end of the array without disturbing the order of the other elements. What is the most efficient way to do this?
Every way I can think of seems a bit clunky and I feel like I'm missing something obvious.
The only way I can think to do this is to remove it then add it:
$v = $my_array['monkey'];
unset($my_array['monkey']);
$my_array['monkey'] = $v;
array_shift is probably less efficient than unsetting the index, but it works:
$my_array = array('monkey' => 1, 'giraffe' => 2, 'lion' => 3);
$my_array['monkey'] = array_shift($my_array);
print_r($my_array);
Another alternative is with a callback and uksort:
uksort($my_array, create_function('$x,$y','return ($y === "monkey") ? -1 : 1;'));
You will want to use a proper lambda if you are using PHP5.3+ or just define the function as a global function regularly.
I really like #Gordon's answer for its elegance as a one liner, but it only works if the targeted key exists in the first element of the array. Here's another one-liner that will work for a key in any position:
$arr = ['monkey' => 1, 'giraffe' => 2, 'lion' => 3];
$arr += array_splice($arr, array_search('giraffe', array_keys($arr)), 1);
Demo
Beware, this fails with numeric keys because of the way that the array union operator (+=) works with numeric keys (Demo).
Also, this snippet assumes that the targeted key is guaranteed to exist in the array. If the targeted key does not exist, then the result will incorrectly move the first element to the end because array_search() will return false which will be coalesced to 0 by array_splice() (Demo).
You can implement some basic calculus and get a universal function for moving array element from one position to the other.
For PHP it looks like this:
function magicFunction ($targetArray, $indexFrom, $indexTo) {
$targetElement = $targetArray[$indexFrom];
$magicIncrement = ($indexTo - $indexFrom) / abs ($indexTo - $indexFrom);
for ($Element = $indexFrom; $Element != $indexTo; $Element += $magicIncrement){
$targetArray[$Element] = $targetArray[$Element + $magicIncrement];
}
$targetArray[$indexTo] = $targetElement;
}
Check out "moving array elements" at "gloommatter" for detailed explanation.
http://www.gloommatter.com/DDesign/programming/moving-any-array-elements-universal-function.html

Categories