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)
}
Related
I got this array:
$array = array('E1211','E2172','E2181','E233','E241','E286');
And I need to order first by first number-letter, i.e. E1, E2, E3, E4... followed by ordered numbers from lowest to highest.
Desired order would be: E1211, E233, E241, E286, E2172, E2181
If i do sort($array); the order will will be: "E1211", "E2172", "E2181", "E233", "E241" "E286".
If i do natsort($array); it will order by numbers from lowest to higest: "E233", "E241", "E286", "E1211", "E2172", "E2181"
Use usort() with a custom comparison function to split the strings and compare the portions.
<?php
$array = array('E1211','E2172','E2181','E233','E241','E286');
usort($array, function($a, $b){
$pfxA = substr($a,0,2); // Get the first two characters of each string
$pfxB = substr($b,0,2);
if ( $pfxA !== $pfxB) {return $pfxA<=>$pfxB;} // If they're not equal, return the spaceship comparison
return (int)substr($a,2)<=>(int)substr($b,2); // Prefixes are equal. Convert the rest to integers and return the spaceship comparison.
});
var_dump($array);
Output:
array(6) {
[0]=>
string(5) "E1211"
[1]=>
string(4) "E233"
[2]=>
string(4) "E241"
[3]=>
string(4) "E286"
[4]=>
string(5) "E2172"
[5]=>
string(5) "E2181"
}
See https://3v4l.org/5qts5
Actually what you want to do is mixing sorting with your own pattern with natsort(). I doubt if it can be accomplished by some oneliner, however it can be done with simple iteration in separate steps, that code does what I think you want (input data modified for better readibility).
<?php
$array = ['E3123', 'E1211', 'E2181', 'E241', 'E286', 'E2172', 'E233'];
$newArray = [];
$finalArray=[];
// Put it into associative array with required keys (two first chars)
foreach ($array as $item) {
$key = substr($item, 0, 2);
$newArray[$key][] = $item;
}
// sort new array by key (two first chars)
ksort($newArray);
// sort each subarray in natural order and put it to final array
foreach ($newArray as $key => $newItem) {
natsort($newArray[$key]);
$finalArray = array_merge($finalArray, $newArray[$key]);
}
// just check
var_dump($finalArray);
Result:
array (size=7)
0 => string 'E1211' (length=5)
1 => string 'E233' (length=4)
2 => string 'E241' (length=4)
3 => string 'E286' (length=4)
4 => string 'E2172' (length=5)
5 => string 'E2181' (length=5)
6 => string 'E3123' (length=5)
I think this should be something simple, but I can't find a way to do it.
I have two arrays, one with colors and ids
$color_ids = [
'green' => [1,2,3],
'blue' => [4,5],
'red' => [6],
'pink' => [7,8,9],
'yellow' => [10]
];
and others with selection.
$select = ['green', 'red', 'yellow'];
Then I need the ids where intersects keys between $color_ids and $select. This should be the result (a simple linear array):
$results = [1,2,3,6,10];
I've tried this:
$result = array_values(array_intersect_key( $color_ids, array_flip($select ) ) );;
But I get multidimensional array:
array(3) {
[0]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
[1]=> array(1) { [0]=> int(6) }
[2]=> array(1) { [0]=> int(10) }
}
I get the same result with this:
$result = [];
foreach ($select as $key) {
if (isset($color_ids[$key]))
$result[] = $color_ids[$key];
}
How can I get a simple linear array with ids?
1) Iterate over the key array
2) Merge all the arrays into one using array_merge
$select = ['green', 'red', 'yellow'];
$finalArray=[];
foreach($select as $value){
$finalArray= array_merge($finalArray,$color_ids[$value]);
}
echo "<pre>";
print_r($finalArray);
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 6
[4] => 10
)
You can use array_merge with the ... operator which is a common flattening technique demonstrated here.
array_merge(...$result);
Array Merge documentation.
Splat Operator documentation.
with foreach
$result = [];
foreach($color_ids as $color => $ids){
$result = array_merge($result, in_array($color, $select) ? $ids : []);
}
If you are looking for a fully "functional style" solution, here's one.
Set up your filtering array ($select) so that it has keys like the master array's keys. In other words, flip the values into keys.
Then swiftly filter the master array using array_intersect_key().
Then to flatten the remaining data, prepend the splat/spread operator and call array_merge().
Until you are on a php version that allows associative arrays to be spread, then you'll need to call array_values() to reindex the first level keys so that the spread operator doesn't choke.
Code: (Demo)
var_export(
array_merge(
...array_values(
array_intersect_key(
$color_ids,
array_flip($select)
)
)
)
);
Or if you prefer jumble the syntax into a single line:
var_export(array_merge(...array_values(array_intersect_key($color_ids, array_flip($select)))));
Although a foreach() may perform a few nanoseconds faster than functional programming, you are never going to notice on such small data sets. One benefit of this coding style is that you don't have to name any temporary variables to push qualifying data into. In other words you can feed this directly into a return statement inside of a function which I find to be very tidy.
Alternatively, if you don't mind generating a result variable, then iterated array_push() calls while spreading the matching nominated color_id value is pretty simple. This technique is different in that if $select contains a color that is not a key in $color_id, then php will bark at you!
Code: (Demo)
$result = [];
foreach ($select as $color) {
array_push($result, ...$color_ids[$color]);
}
var_export($result);
Both techniques offer the same result.
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
Can someone please explain to me whats the difference between these 2 functions:
array_diff_ukey
array_diff_uassoc
They both take keys into the compare function, and based on those keys decide if the array element should be returned or not. I've checked the php manual, but to me they both seem to be doing the same thing...
array_diff_ukey returns those elements of the first array whose keys compare different to all keys in the second array (the semantics of the comparison being user-defined). The values associated with those keys do not play a part.
array_diff_uassoc is a "more inclusive" version of the above that also checks values: if a key in the first array compares equal to a key in the second array but the values are different, that element is also included in the result. In this case, comparison of values is not user-defined, but works as in array_diff: for two values to compare equal, their string representation must be identical.
Example, adapted from the PHP docs:
function key_compare_func($key1, $key2)
{
if ($key1 == $key2)
return 0;
else if ($key1 > $key2)
return 1;
else
return -1;
}
$array1 = array('blue' => 1, 'red' => 2, 'green' => "3", 'purple' => 4);
$array2 = array('green' => 3, 'blue' => 6, 'yellow' => 7, 'cyan' => 8);
var_dump(array_diff_ukey($array1, $array2, 'key_compare_func'));
var_dump(array_diff_uassoc($array1, $array2, 'key_compare_func'));
See it in action.
Here, array_diff_ukey will return the "red" and "purple" elements from $array1 because these keys do not compare equal to any of the keys in $array2. However array_diff_uassoc will also return the "blue" element, because even though that key exists in both arrays the associated values are different.
Note that the "green" element is not included in either result, despite the fact that the associated value is a string in $array1 and an integer in $array2.
From the manual:
array_diff — Computes the difference of arrays
array_diff_key — array_diff_key — Computes the difference of arrays using keys for comparison
array_diff_assoc — Computes the difference of arrays with additional index check
This additional index check means that not only the value must be the same, but also the key must be the same. So the difference between array_diff_ukey and array_diff_uassoc is that the latter checks both keys and values, while the first only checks the keys.
The addition of the u after diff_ means that you must supply a custom callback function instead of the default built-in function.
Example based on the manual (Fiddle)
<?php
header("Content-Type: text/plain");
$array1 = array('blue' => 1, 'red' => 2, 'green' => 3, 'black' => 0, 'purple' => 4);
$array2 = array('green' => 5, 'blue' => 6, 'yellow' => 7, 'cyan' => 8, 'black' => 0);
var_dump(array_diff($array1, $array2));
var_dump(array_diff_key($array1, $array2));
var_dump(array_diff_assoc($array1, $array2));
?>
Output
array(4) {
["blue"]=>
int(1)
["red"]=>
int(2)
["green"]=>
int(3)
["purple"]=>
int(4)
}
array(2) {
["red"]=>
int(2)
["purple"]=>
int(4)
}
array(4) {
["blue"]=>
int(1)
["red"]=>
int(2)
["green"]=>
int(3)
["purple"]=>
int(4)
}
Hi I have an array, I need to change the keys, in an orderly manner but don't change the order the values are. e.g.
$a = array (
0=>'h',
1=>'blabla',
2=>'yes'
);
I used
unset($a[1]);
but i need the key to restart calculating the keys
0,1,2 ... etccc
so i don't end up with:
array(
0 => 'h',
2 => 'yes'
)
but it should come return:
array(
0 => 'h',
1 => 'yes'
)
You need to apply array_values on your array to re-index.
$a = array_values($a);
Bonus: If you also need to order your values you can use sort and it too will re-index your array.
Note: By using any of array_values or sort you will loose any string keys you may have.
Call array_values on it:
$a = array (
0=>'h',
1=>'blabla',
2=>'yes'
);
unset($a[1]);
$a = array_values($a);
var_dump($a);
/*
array(2) {
[0]=>
string(1) "h"
[1]=>
string(3) "yes"
}
*/
You can also use array_splice() instead of unset(), which will automatically reindex the array elements:
$a = array (
0=>'h',
1=>'blabla',
2=>'yes'
);
array_splice($a,1,1);
var_dump($a);