array_uintersect with sub-arrays doesn't intersect first item - php

I have created the following script for creating an array_uintersect alogrithm.
function compare2D($topic, $nomination): int
{
if (is_array($nomination)) {
return in_array($topic, $nomination) ? 0 : 1;
}
else if (is_array($topic)) {
return in_array($nomination, $topic) ? 0 : 1;
}
return strcmp($topic, $nomination);
}
$arr = [['REMOTE', 'REMOTE_PREMIUM'], 'SECURE'];
$topic = ['SECURE', 'REMOTE'];
var_dump(array_uintersect($topic, $arr, 'compare2D'));
When I run it, the result is
array(1) {
[0] =>
string(6) "SECURE"
}
Where it should be returning
array(2) {
[0] =>
string(6) "SECURE"
[1] =>
string(6) "REMOTE"
}
I found that this is only dependent on the array in 2nd argument to _uintersect having a sub-array at index 0 (first item).
When I move the sub-array to any position except for the 1st, i.e:
$arr = ['SECURE', ['REMOTE', 'REMOTE_PREMIUM']];
// or
$arr = ['SOMEVALUE', ['REMOTE', 'REMOTE_PREMIUM'], 'SECURE'];
...the intersection works fine, and I get the intended result above.
Does anyone know some algorithmic rules of php intersection that I am not aware of?

I think you can switch the logic of returning 1 and 0, so return 1 when the value is in the array as you want to find the intersection.
For the last part, using strcmp will return 0 if the strings are equal. In that case you can check if they are equal, and if they are, then return 1.
function compare2D($topic, $nomination): int
{
if (is_array($nomination)) {
return in_array($topic, $nomination) ? 1 : 0;
}
if (is_array($topic)) {
return in_array($nomination, $topic) ? 1 : 0;
}
return strcmp($topic, $nomination) === 0 ? 1 : 0;
}
$topic = ['SECURE', 'REMOTE'];
$arr = [['REMOTE', 'REMOTE_PREMIUM'], 'SECURE'];
$res = array_uintersect($topic, $arr, 'compare2D');
var_dump($res);
Output
array(2) {
[0]=>
string(6) "SECURE"
[1]=>
string(6) "REMOTE"
}
Php demo | Php demo with more nested arrays

Related

Php group repeated array values

How to group repeated array values in an array using PHP?
I have an array like this
array[0]=203,
array[1]=204,
array[2]=204,
array[3]=203,
array[4]=203,
array[5]=205
I need results like
[203]=1,
[204]=2,
[203]=2,
[205]=1
i want the count of continuously repeating array values
One option to your expected output is to create a indexed array with the associative array below it.
This will create this kind of array:
array(4) {
[0]=>
array(1) {
[203]=>
int(1)
}
[1]=>
array(1) {
[204]=>
int(2)
}
[2]=>
array(1) {
[203]=>
int(2)
}
[3]=>
array(1) {
[205]=>
int(1)
}
}
This is not what you wanted but it is what is possible.
The code loops and keeps track of what the previous value is, if it's the same it will count up the value, else create a new indexed and associative array with value 1.
$array =[203,204,204,203,203,205];
$i=-1;
$prev = null;
$new=[];
foreach($array as $val){
if($val != $prev){
$i++;
}
if(!isset($new[$i][$val])){
$new[$i][$val] =1;
}else{
$new[$i][$val]++;
}
$prev = $val;
}
var_dump($new);
https://3v4l.org/W2adN
array[0]=203;
array[1]=204;
array[2]=204;
array[3]=203;
array[4]=203;
array[5]=205;
$new_array = array();
foreach ($array as $key => $value) {
if(empty($new_array[$value])){
$new_array[$value] = 1;
}else{
$new_array[$value]++;
}
}
/*Now in the array $new_array you have the count of continuously repeating array values*/
This can be done in one line of code using array_count_value function in php.
$arr[0]=203;
$arr[1]=204;
$arr[2]=204;
$arr[3]=203;
$arr[4]=203;
$arr[5]=205;
$result = array_count_values( $arr );
var_dump( $result );
output
array (size=3)
203 => int 3
204 => int 2
205 => int 1

Normalize array values in natural order PHP

I have faced with the problem, I need to normalize/sort in natural order values in array after some item has been removed.
Consider following example. Initial array
{ [313]=> int(2) [303]=> int(1) [295]=> int(3) [290]=> int(4) }
Sorted array
{ [303]=> int(1) [313]=> int(2) [295]=> int(3) [290]=> int(4) }
Consider case when we are removing first item, array should look like this now
{ [313]=> int(1) [295]=> int(2) [290]=> int(3) }
In case of item inside the array range for example 295 (3) it should be
{ [303]=> int(1) [313]=> int(2) [290]=> int(3) }
I hope you get an idea.
But my function doesn't do this correctly.
I've implemented part of this sorting, here is the code, but maybe there are other ways to do this easier ?
const MIN_VALUE = 1;
public function sort_items(&$items_map)
{
if (!empty($items_map)) {
asort($items_map);
var_dump($items_map);
$first_item = reset($items_map);
if ($first_item > self::MIN_VALUE) {
$normalize_delta = $first_item - self::MIN_VALUE;
$prev_item_id = null;
foreach ($items_map as $id => $part) {
$items_map[$id] = $part - $normalize_delta;
if (!empty($prev_item_id)) {
$difference = $items_map[$id] - $items_map[$prev_item_id];
if ($difference > 1) {
$items_map[$id] = $items_map[$id] - ($difference - 1);
}
}
$prev_item_id = $id;
}
}
}
return $items_map;
}
I would be grateful for any help.
Thanks
UPDATE
To clarify.
I want items not to be just sorted in the correct order, but to be in natural order, for example
Sequence 1,3,5,6,7,9 should be transformed into 1,2,3,4,5,6 but keeping keys the same.
2,3,7,9 => 1,2,3,4
Please see my example above with real word case.
If you need to use a custom sort algorithm, use usort to do so. From PhP the documentation :
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
So you just need to provide those integers if you are in case an item is "greater" or "lower", and usort will do the job for you.
In your case, it could lead to this function :
<?php
function sort_items_map($a, $b)
{
$value = 0;
if( $a < $b )
{
$value = -1;
}
else if( $a > $b )
{
$value = 1;
}
else if( $a == $b )
{
$value = 0;
}
return $value;
}
$items_map = [1, 3, 1, 7]; // or fill it with your own values
usort($items_map, "sort_items_map");
?>

Changing values of multidimensional array php

Its my first time working with multidimensional arrays in php. I need to change the second number in each sub array.
What I want is to check if the Id in the array matches the Id from the database. When the two match I want to change the 2nd entry in the sub array by adding a number to it. If the Id from the query does not match anything in the list I want a new sub array to be pushed to the end of the array with the values of Id and points_description.
Also, if its helpful, my program right now does find the matches. The only thing is, it does not update the 2D array.
$array = array(array());
while ($row_description = mysqli_fetch_array($query_description)) {
$check = 1;
$is_match = 0;
foreach ($array as $i) {
foreach ($i as $value) {
if ($check == 1) {
if ($row_description['Id'] == $value) {
//$array[$i] += $points_description;
$is_match = 1;
}
}
$check++;
$check %= 2; //toggle between check and points
}
}
if ($is_match == 0) {
array_push($array, array($row_description['Id'], $points_description));
}
}
I feel like Im doing this so wrong. I just want to go through my 2D array and change every second value. The expected output should be a print out of all the Ids and their corresponding point value
I hope this is helpful enough.
Example: $row_description['Id'] = 2 and $array = array(array(2,1), array(5,1) , array(6,1))
output should be $array = array(array(2,4), array(5,1) , array(6,1))
if $row_description['Id'] = 3 and $array = array(array(2,1), array(5,1) , array(6,1))
output should be $array = array(array(2,4), array(5,1) , array(6,1),array(3,3))
By default PHP will copy an array when you use it in a foreach.
To prevent PHP from creating this copy you need to use to reference the value with &
Simple example :
<?php
$arrFoo = [1, 2, 3, 4, 5,];
$arrBar = [3, 6, 9,];
Default PHP behavior : Make a copy
foreach($arrFoo as $value_foo) {
foreach($arrBar as $value_bar) {
$value_foo *= $value_bar;
}
}
var_dump($arrFoo);
/* Output :
array(5) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
[3]=>
int(4)
[4]=>
int(5)
}
*/
ByReference : Don't create the copy :
foreach($arrFoo as &$value_foo) {
foreach($arrBar as $value_bar) {
$value_foo *= $value_bar;
}
}
var_dump($arrFoo);
/* Output :
array(5) {
[0]=>
int(162)
[1]=>
int(324)
[2]=>
int(486)
[3]=>
int(648)
[4]=>
&int(810)
}
*/

Continue to next for loop if current loop is 0

It seems this is a general question of programming logic, as this issue seems to arise in my code regardless of what language it's coded in.
Basically I have 2 nested for loops, inside a for loop. The purpose of these for loops is to enumerate all possible values between two sets of values.
The problem with the code is that is the second set of values contains a 0 the values won't enumerate.
Say for example we want to enumerate all values between 0,0,0 and 1,1,1, this works perfectly fine, as each nested loop is processed. However is we try to calculate between 0,0,0 and 0,1,0 the loop will not continue on to the next loop, instead it will exit the loop and continue on to the rest of the code.
for ($i1=$coords1[0]; $i1<=$coords2[0]; $i1++) { //if $coords2[0] = 0 loop will fail
for ($i2=$coords1[1]; $i2<=$coords2[1]; $i2++) { //if $coords2[1] = 0 loop will fail
for ($i3=$coords1[2]; $i3<=$coords2[2]; $i3++) {//if $coords2[2] = 0 loop will fail
$blocks.= $i1.",".$i2.",".$i3."|";
}
}
}
return $blocks;
Full code: PHPFIDDLE
Okay, to copy this over from our chat, I believe this is the solution:
<?php
$set1 = explode(",", '1,0,1');
$set2 = explode(",", '1,1,0');
$allbetween = _allbetween($set1, $set2);
echo 'All Between: '.$allbetween.'<br/>';
$allcount = count(explode("|", $allbetween))-1;
echo 'Number Of Blocks: '.$allcount;
function _allbetween($coords1, $coords2) {
$blocks = "";
for ($i=0; $i<=2; $i++) {
if ($coords1[$i] > $coords2[$i]) {
$tmp = $coords1[$i];
$coords1[$i] = $coords2[$i];
$coords2[$i] = $tmp;
}
}
for ($i1=$coords1[0]; $i1<=$coords2[0]; $i1++)
{
for ($i2=$coords1[1]; $i2<=$coords2[1]; $i2++)
{
for ($i3=$coords1[2]; $i3<=$coords2[2]; $i3++)
{
$blocks.= $i1.",".$i2.",".$i3."|";
}
}
}
return $blocks;
}
?>
DEMO HERE
The reason this works is that there is a swap loop at the beginning of your function, which swaps any of the three sets of values if the first is greater than the second. This ensures that all values "between" them can be calculated.
Edit: Fixing the demo link to the correct URL
the break; will do. see more information in the link:
http://www.php.net/manual/en/control-structures.break.php
Your loops will never loop as you are setting each initial value to the end range value. Instead use:
for ($i1=0; $i1<=$coords2[0]; $i1++)
...
etc
In terms of fall-through, try the following:
for ($i1=0; $i1<=$coords2[0] + 1; $i1++)
...
etc
Here's the problem: Dumping out your two $coords arrays:
$coords1:
array(3) {
[0]=>
string(1) "0"
[1]=>
string(1) "0"
[2]=>
string(1) "1"
}
$coords2:
array(3) {
[0]=>
string(1) "1"
[1]=>
string(1) "0"
[2]=>
string(1) "1"
}
On your first iteration:
$coords1[0] => 1
$coords2[0] => 0
1 <= 0 -> FALSE
so your outermost loop NEVER executes at all.
I think you are searching for
GOTO function
And for the
Continue function

php pushing pattern from array1 to array2

I have an array that looks something like this
array(7) {
[0]=> "hello,pat1"
[1]=> "hello,pat1"
[2]=> "test,pat2"
[3]=> "test,pat2"
[4]=> "foo,pat3"
[5]=> "foo,pat3"
[6]=> "foo,pat3"
....
}
I would like to push it into another array so the output of the array2 is as follow:
array(7) {
[0]=> "hello,pat1"
[1]=> "test,pat2"
[2]=> "foo,pat3"
[3]=> "foo,pat3"
[4]=> "foo,pat3"
[5]=> "hello,pat1"
[6]=> "test,pat2"
.....
}
What I want is to push them in the following pattern: 1 "pat1" 1 "pat2" and 3 "pat3", and repeat this pattern every 5 elements.
while ( !empty( $array1 ) )
$a = explode(",",$array1[$i]);
if($a[1]=='pat1' &&)
push && unset
elseif($a[1]=='pat2' &&)
push && unset
elseif($a[1]=='pat3' and < 5)
push && unset and reset pattern counter
}
What would be a good way of doing this?
Any idea will be appreciate it.
Time for some fun with the iterators of the Standard PHP Library :-)
<?php
$array1 = array (
"hello1,pat1", "hello2,pat1", "hello3,pat1",
"test1,pat2", "test2,pat2",
"foo1,pat3", "foo2,pat3", "foo3,pat3",
"foo4,pat3", "foo5,pat3", "foo6,pat3"
);
// "group by" patN
$foo = array();
foreach($array1 as $a) {
// feel free to complain about the # here ...to somebody else
#$foo[ strrchr($a, ',') ][] = $a;
}
// split pat3 into chunks of 3
$foo[',pat3'] = array_chunk($foo[',pat3'], 3);
// add all "groups" to a MultipleIterator
$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
foreach($foo as $x) {
$mi->attachIterator( new ArrayIterator($x) );
}
// each call to $mi->current() will return an array
// with the current items of all registered iterators
foreach ($mi as $x) {
// "flatten" the nested arrays
foreach( new RecursiveIteratorIterator(new RecursiveArrayIterator($x)) as $e) {
echo $e, "\n";
}
echo "----\n";
}
I think I'd set respective counters with respective incrementation. Since php will cast float keys to integers, you can just increment the ,pat3 items with .33 instead of 1. Then, to flatten the result, just use array_merge() with the splat operator.
Code: (Demo)
$array = [
"hello1,pat1",
"hello2,pat1",
"test3,pat2",
"test4,pat2",
"foo5,pat3",
"foo6,pat3",
"foo7,pat3",
];
$counters = [',pat1' => 0, ',pat2' => 0, ',pat3' => 0];
foreach ($array as $value) {
$end = strrchr($value, ',');
$groups[$counters[$end]][] = $value;
$counters[$end] += ($end === ',pat3' ? .33 : 1);
}
var_export(array_merge(...$groups));
Output:
array (
0 => 'hello1,pat1',
1 => 'test3,pat2',
2 => 'foo5,pat3',
3 => 'foo6,pat3',
4 => 'foo7,pat3',
5 => 'hello2,pat1',
6 => 'test4,pat2',
)

Categories