Order one array item without breaking its key order - php

I already saw a lot of questions that ask about the topic but I am unable to solve my problem.
I am fetching an array with menu id items from database and those items can be sometimes be updated.
The array of ids I receive from db is:
array(3) {
[0]=>
int(1)
[1]=>
int(6)
[2]=>
int(2)
}
And I need id 2 (int) to move into the key position 0 ($sortindex) without breaking the array items order. So the desire result would be:
array(3) {
[0]=>
int(2)
[1]=>
int(1)
[2]=>
int(6)
}
For that I am trying to use usort but without success:
usort($data, function($a, $b) use ($sortindex){
if ($a == $b) {
return 0;
}
if ($a == $sortindex) {
return 1;
}
});
This could happen to any other item id. For example I could order id 1 from 0 key position to 2 and the result would be:
array(3) {
[0]=>
int(6)
[1]=>
int(2)
[2]=>
int(1)
}

$array = [1, 6, 2];
array_unshift($array, array_pop($array));
Or possibly:
$array = [1, 6, 2];
$tmp = array_splice($array, 2, 1);
array_unshift($array, $tmp[0]);
You're not really looking for sorting on values, you just want to swap indices.
If you want to insert the value somewhere other than the front of the array, splice it back in with array_splice.
If you have more keys that you need to change around, then sorting may after all become the simplest solution:
uksort($array, function ($a, $b) {
static $keyOrder = [2 => 0, 0 => 1, 1 => 2];
return $keyOrder[$b] - $keyOrder[$a];
});

Perhaps you are seeking asort function

Related

Compare only values of arrays and get difference

I have two arrays like below;
array1 = {
[0]=> 'A'
[1]=> 'B'
[2]=> 'C'
}
array2 = {
[0]=> 'B'
[1]=> 'C'
[2]=> 'D'
}
I want to compare these two arrays and get the difference.
What I need return is like this;
result = {
[0]=> 'A'
[1]=> 'D'
}
I thought I could use array_diff, but it compares key AND value as a pair. I want to compare ONLY values.
How can I do that?
This should work -
array_diff(array_merge($a, $b), array_intersect($a, $b))
Checking difference between all merged and all common.

How do I compare a string array to a numeric array?

dear users of StackOverflow. There is some problem.
Array 1:
array: 3 [▼
   0 => "8"
   1 => "13"
   2 => "15"
]
Array 2:
array: 16 [▼
   0 => 7
   1 => 8
   2 => 9
]
array_diff does not work, because in the first number, in the second string.
Please suggest any ideas for solving the issue. I will be happy with any comment. Many thanks.
You can use array_udiff to compare the arrays using a user-defined callback which converts the values to ints before comparing:
$array1 = array('8', '13', '15');
$array2 = array(7, 8, 9);
$diffs = array_udiff($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
print_r($diffs);
Output:
Array
(
[1] => 13
[2] => 15
)
Update
It has been pointed out that the desired output hasn't been specified, so here is how to get all unique values:
$diffs1 = array_udiff($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
$diffs2 = array_udiff($array2, $array1, function ($a, $b) { return (int)$a - (int)$b; });
$diffs = array_merge($diffs1, $diffs2);
print_r($diffs);
Output:
Array
(
[0] => 13
[1] => 15
[2] => 7
[3] => 9
)
and all matching values using array_uintersect:
$same = array_uintersect($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
print_r($same);
Output:
Array
(
[0] => 8
)
Note
In PHP7 there is now the spaceship operator (<=>) which can also be used in the comparison function e.g.
$diffs = array_udiff($array1, $array2, function ($a, $b) { return (int)$a <=> (int)$b; });
You could convert the arrays using array map like this
$a1 = array_map('intval', $a1);
$a2 = array_map('intval', $a2);
Then do your array diff and what not.
#Nick's solution is a bit more elegant.
Because, it's not walking the arrays 2x more then you really need to. Of course if you know which is string then you could just convert that one, but I thought I would post another way to do it...
For testing you can simply do this
$a = [
"1",
"2" ,
"3"
];
var_dump($a);
var_dump(array_map('intval', $a));
Output
array(3) {
[0]=> string(1) "1"
[1]=> string(1) "2"
[2]=> string(1) "3"
}
array(3) {
[0]=> int(1)
[1]=> int(2)
[2]=> int(3)
}
Sandbox
And this shows that it does convert the values to string, which was pretty obvious, but I like examples. So there you go.
Cheers!
UPDATE
After doing some simple bench marking, with an array of 100,000 string numbers, and taking the average time from 100 iterations, it took apx 0.0072/seconds to convert the array back to ints:
//setup
$a = array_map('strval', range(0, 100000));
//get microtime as float after setup
$time = microtime(true);
//use the average of 100 conversion for consistency
$iterations = 100;
for($i=0;$i<$iterations; ++$i){
$b = array_map('intval', $a); //set to $b so we don't convert $a on our first iteration.
//check the first iteration, to make sure we have it setup correctly for our averaging
if($i==0)
echo number_format(
((microtime(true) - $time)),
4
)." \seconds\n\n";
}
echo number_format(
((microtime(true) - $time) / $itterations),
4
)." \seconds\n";
Output
0.0067 \seconds
//if these are close (which they are) we have everything setup right,
//it's better to take the average for speed testing.
//and I just wanted to double check that it was being done correctly
0.0072 \seconds
Sandbox
-note- the sandbox has only 134M of Ram for PHP (i've run it out of memory on purpose to test it.. lol)
<b>Fatal error</b>: Allowed memory size of 134217728 bytes exhausted
UPDATE1
It has been pointed out that the desired output hasn't been specified, so here is how to get all unique values:
If you want the Unique values from both arrays you can do
$unique = array_unique(array_replace($a1,$a2));
And if the arrays are unique beforehand you can just do array_replace because you will be combining 2 unique arrays replacing any in the one array that are duplicated in the other. Therefore the result will be the unique combination of 2 unique arrays, if that makes sense.
This is a non-issue. PHP DOES NOT require a workaround for string numbers versus non-string numbers.
Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In other words: when the string representation is the same.
Source: https://www.php.net/manual/en/function.array-diff.php
Here's my output in PHP 7.4.7. (The same output happens when I run it in PHP 5.6, and that's the earliest version I have installed right now.)
php > var_dump(array_diff([1,2,3], ["2"]));
array(2) {
[0]=>
int(1)
[2]=>
int(3)
}

PHP - custom define order of array of objects ie C1,C2,C3 to C2,C1,C3

I have an array similar to the one below
I need to sort this array into a certain order eg.
How can I custom order by the name property into this order C2, C1, C3 for example?
array(11) {
[0]=>
object(stdClass)#1569 (8) {
["name"]=>
string(36) "C1"
}
[1]=>
object(stdClass)#1589 (8) {
["name"]=>
string(36) "C2"
}
[2]=>
object(stdClass)#1599 (8) {
["name"]=>
string(36) "C3"
}
}
Define your custom order in an array.
$order = ['C2' => 0, 'C1' => 1, 'C3' => 2];
Then use that array in the comparison callback to usort.
usort($objects, function($ob1, $ob2) use ($order) {
return $order[$ob1->name] <=> $order[$ob2->name];
});
If it's possible that some of the objects won't have a name property, or that they may have name properties that aren't included in the custom order, we can set default values so that any of those will be sorted to the end. (This will prevent "Undefined Index" and "Undefined Property" notices as well.)
usort($objects, function($ob1, $ob2) use ($order) {
return ($order[($ob1->name ?? '')] ?? end($order) + 1)
<=> ($order[($ob2->name ?? '')] ?? end($order) + 1);
});

passing null as array_keys second parameter

PHP documentation states that the default value of array_keys second argument is NULL.
However, when passing NULL explicitly, array_keys does not seem to work properly.
Example:
code
$a = array(10=>'a', 11=>'b', 12=>'b', 13=>'c', 14=>'c', 15=>'b');
$keys = array_keys($a);
var_dump($keys); //Output 0
$keys = array_keys($a, null);
var_dump($keys); //Output 1
Output
array
0 => int 10
1 => int 11
2 => int 12
3 => int 13
4 => int 14
5 => int 15
array
empty
Question
I reckon it must be trying to find keys whose value is null.
Passing false or empty string produces the exact same behavior (obviously).
So, what is the default value?
Answer
xdazz answer is right. When inspecting the C code of this function, my first though was that this was a bad implementation at C level (and easily fixed by removing code)
But then I realized this is actually the intended behavior since they went to a lot of trouble to enable you to test for NULL values inside the array.
The default value is hard to explain here.
It is special case, the default value of second parameter is not actually the php's NULL, but the C level's NULL.
Dive into the source code:
PHP_FUNCTION(array_keys)
{
zval *input, /* Input array */
*search_value = NULL, /* Value to search for */
//....
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) {
return;
}
// ....
You could see, the default value is NULL for search_value, but if you specified the second parameter of array_keys, then after zend_parse_parameters, search_value will not be NULL.
I agree with xdazz. However, it seems that passing 0 works. Don't ask me how, though! (And, YMMV.)
$a = array(10=>'a', 'xaa'=>'b', 12=>'b', 13=>'c', 14=>'c', 15=>'b');
$keys = array_keys($a, 0);
var_dump($keys); //Output 0
Returns:
array(6) { [0]=> int(10) [1]=> string(3) "xaa" [2]=> int(12) [3]=> int(13) [4]=> int(14) [5]=> int(15) }
Since you're searching for null, there's no result to give back to you :)
It is hard to infer from PHP manual as to the use of search_value parameter. I needed to do try an example.
If the optional search_value is specified,
then only the keys for **that** value are returned.
Otherwise, all the keys from the input are returned.
It means that when search_value is specified in array_keys(), the values in the array (not keys) are searched against search_value. If match occurs, the key for that value in search_value is returned.
Example
<?
$foo = array('first' => 'my_first',
'second' => 'my_second',
'third' => 'my_third',
'fourth' => null);
$keys = array_keys($foo);
var_dump($keys); //Output 0
$keys = array_keys($foo, 'my_second');
var_dump($keys); //Output 1
$keys = array_keys($foo, null);
var_dump($keys); //Output 2
?>
Output
0:
array(4) {
[0]=>
string(5) "first"
[1]=>
string(6) "second"
[2]=>
string(5) "third"
[3]=>
string(6) "fourth"
}
1:
array(1) {
[0]=>
string(6) "second"
}
2:
array(1) {
[0]=>
string(6) "fourth"
}

symfony: question about a choice widget

I have a choice widget in a form:
$years = range(14,130);
$this->widgetSchema['age'] = new sfWidgetFormSchema();
foreach (array('from', 'to') as $value)
{
$this->widgetSchema['age'][$value] = new sfWidgetFormChoice(array(
'label' => $value,
'choices' => array_merge(array('' => '-'),array_combine($years,$years))));
}
If i choose for example 14 and in the action that receives the form is written something like this:
var_dump($valores_widgets['age']['from']);
that shows 0. But I expected 14.
Any idea?
Regards
Javi
Check the documentation of array_merge:
If, however, the arrays contain
numeric keys, the later value will not
overwrite the original value, but will
be appended.
If all of the arrays contain only
numeric keys, the resulting array is
given incrementing keys starting from
zero.
A quick test:
maerlyn#biliskner:~$ php --run '$years=range(14,16);var_dump(array_merge(array("" => "-"), array_combine($years,$years)));'
array(4) {
[""]=>
string(1) "-"
[0]=>
int(14)
[1]=>
int(15)
[2]=>
int(16)
}
So your $years array gets reindexed during the merge. When using the + operator instead:
maerlyn#biliskner:~$ php --run '$years=range(14,16);var_dump(array("" => "-") + array_combine($years,$years));'
array(4) {
[""]=>
string(1) "-"
[14]=>
int(14)
[15]=>
int(15)
[16]=>
int(16)
}

Categories