I am trying to left rotate an array in PHP 2 times. It works correctly but there is a blank space in the array from some unknown reason. This is my code:
$a_temp = fgets($handle);
$a = explode(" ",$a_temp);
for($i = 0; $i < 2; $i++){
print_r($a);
array_unshift($a, array_pop($a));
print_r($a);
}
The file has something like this:
1 2 3
Now the output I get is:
Array
(
[0] => 1
[1] => 2
[2] => 3
)
Array
(
[0] => 3
[1] => 1
[2] => 2
)
Array
(
[0] => 3
[1] => 1
[2] => 2
)
Array
(
[0] => 2
[1] => 3
[2] => 1
)
As you can see, every time a rotate is performed, it introduces a blank space in it and during printing the array, it appears as a new line character. Any ideas what I am doing wrong?
As pointed out by #cHao ans #Kulvar, your "3" element is in fact "3\n" because your line returned by fgets ends with a \n which is normal.
Replace $a = explode(" ",$a_temp); with $a = explode(" ",trim($a_temp)); and you're fixed. Works with any string, numeric or not
It seems you have a non-integer value in there. Use array_map() to cast all values to integers.
$a_temp = fgets($handle);
$a = explode(" ",$a_temp);
$a = array_map("intval", $a); //Cast all values to integers
for($i = 0; $i < 2; $i++){
print_r($a);
array_unshift($a, array_pop($a));
print_r($a);
}
https://eval.in/724912
Combine the array_push() and array_shift() function. In this solution you would have to input a number of rotations, or you change the $k variable in the for-loop to your desired amount of rotations.
for($i=0; $i<$k; $i++){
//remove first element
$firstElement = array_shift($a);
//push first element to the end
array_push($a, $firstElement);
}
echo implode(" ", $a);
The first method is the optimised code. But other two are solution but not optimise as the first one.
function rotate_left_method_1( $a, $d ) {
$first = array_slice( $a, 0, $d );
$second = array_slice( $a, $d, null );
return array_merge( $second, $first );
}
function rotate_left_method_2( $a, $d ) {
$swap = 1;
foreach ( $a as $v ) {
if ( $d >= $swap ) {
array_shift( $a );
array_push( $a, $v );
$swap ++;
}
}
return $a;
}
function rotate_left_method_3( $a, $d ) {
if ( is_array( $a ) ) {
for ( $swap = 0; $swap < $d; $swap ++ ) {
$temp = $a[0];
array_shift( $a );
array_push( $a, $temp );
}
}
return $a;
}
var_dump( rotate_left_method_1( array( 1, 2, 3, 4, 5 ), 4 ) );
var_dump( rotate_left_method_2( array( 1, 2, 3, 4, 5 ), 4 ) );
var_dump( rotate_left_method_3( array( 1, 2, 3, 4, 5 ), 4 ) );
function rotateLeft($d, $arr) {
$remaining = array_slice($arr, $d);
array_splice($arr, $d);
return array_merge($remaining,$arr);
}
$d =4; $arr = [1,2,3,4,5];
$result = rotateLeft($d, $arr);
var_dump($result);
output [5, 1, 2, 3, 4]
Related
From the code below I can compare the 2 arrays and find out the $subset elements position range in $array.
$array = [8,2,3,7,4,6,5,1,9];
$subset = [6,3,7];
function get_range($array, $subset)
{
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
return [$min, $max];
}
$range = get_range($array, $subset); // result as an array -> [2, 5]
However, I want to do a recursive array_search for my multidimentional array like:
$subset = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
How can I do this? Expecting results -> [2, 5], [1, 8], [2, 6].
You do not need a recursion here, simply add an additional loop in the get_range() function. The following example is based on your code and is a possible solution to your problem:
<?php
$array = [8,2,3,7,4,6,5,1,9];
$subsets = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
function get_range($array, $subsets)
{
$result = array();
foreach ($subsets as $subset) {
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
$result[] = [$min, $max];
}
return $result;
}
$range = get_range($array, $subsets);
echo print_r($range, true);
?>
Result:
Array (
[0] => Array ( [0] => 2 [1] => 5 )
[1] => Array ( [0] => 1 [1] => 8 )
[2] => Array ( [0] => 2 [1] => 6 )
)
I have associative array like below
$arr = [1=>0, 2=>1, 3=>1, 4=>2, 5=>2, 6=>3]
I would like to remove the duplicate values from the initial array and return those duplicates as as a new array of duplicate arrays. So I would end up with something like;
$arr = [1=>0, 6=>3]
$new_arr = [[2=>1, 3=>1],[4=>2, 5=>2]]
Does PHP provide such a function or if not how would I achieve this?
I've tried;
$array = [];
$array[1] = 5;
$array[2] = 5;
$array[3] = 4;
$array[5] = 6;
$array[7] = 7;
$array[8] = 7;
$counts = array_count_values($array);
print_r($counts);
$duplicates = array_filter($array, function ($value) use ($counts) {
return $counts[$value] > 1;
});
print_r($duplicates);
$result = array_diff($array, $duplicates);
print_r($result);
This outputs;
[1] => 5
[2] => 5
[7] => 7
[8] => 7
&
[3] => 4
[5] => 6
which is almost what I want.
Code
The following works for me... Tho I make no promises in regards to complexity and performance, but there's the general idea... Also, I haven't written PHP for many years now, so bear that in mind.
<?php
function nubDups( $arr ) {
$seen = [];
$dups = [];
foreach ( $arr as $k => $v) {
if ( array_key_exists( $v, $seen ) ) {
// duplicate found!
if ( !array_key_exists( $v, $dups ) )
$dups[$v] = [$seen[$v]];
$dups[$v][] = $k;
} else
// First time seen, record!
$seen[$v] = $k;
}
$uniques = [];
foreach ( $seen as $v => $k ) {
if ( !array_key_exists( $v, $dups ) ) $uniques[$k] = $v;
}
return [$uniques, $dups];
}
function nubDups2( $arr ) {
for ( $seen = $dups = []; list( $k, $v ) = each( $arr ); )
if ( key_exists( $v, $dups ) ) $dups[$v][] = $k;
else if ( key_exists( $v, $seen ) ) $dups[$v] = [$seen[$v], $k];
else $seen[$v] = $k;
return [array_flip( array_diff_key( $seen, $dups ) ), $dups];
}
$arr = [0, 1, 4, 1, 2, 2, 3];
print_r( nubDups( $arr ) );
print_r( nubDups2( $arr ) );
Output (for both)
$ php Test.php
Array
(
[0] => 0
[2] => 4
[6] => 3
)
Array
(
[1] => Array
(
[0] => 1
[1] => 3
)
[2] => Array
(
[0] => 4
[1] => 5
)
)
Shortened
removed, specified as [(k, v)]: [(0, 0), (2, 4), (6, 3)]
duplicates, specified as [(v, [k])]: [(1, [1, 3]), (2, [4, 5])]
In Haskell
This version abuses hash tables for fast lookups.
A simpler version that almost does the same but ignores indexes, written in haskell:
-- | 'nubDupsBy': for a given list yields a pair where the fst contains the
-- the list without any duplicates, and snd contains the duplicate elements.
-- This is determined by a user specified binary predicate function.
nubDupsBy :: (a -> a -> Bool) -> [a] -> ([a], [a])
nubDupsBy p = foldl f ([], [])
where f (seen, dups) x | any (p x) seen = (seen, dups ++ [x])
| otherwise = (seen ++ [x], dups)
I have the following code:
$a = array();
$b = array('a', 'b');
for($i=0; $i<3; $i++){
$a[] = array($b[$i] => array(1, 2, 3));
}
print_r($a);
I get the following result:
Array
(
[0] => Array
(
[a] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
)
[1] => Array
(
[b] => Array
(
[0] => 1
[1] => 2
[1] => 3
)
)
)
This is what I'm trying to accomplish:
array (
'a' => array ( 1, 2, 3 )
'b' => array ( 1, 2, 3 )
)
What am I doing wrong? I don't want $a to add numeric elements, but rather contain a, b, c as the indexes. Any suggestions? Thanks
change the forloop to like this
for($i=0; $i<count($b); $i++){
$a[$b[$i]] =array(1, 2, 3);
}
You can do,
$a = array();
$b = array('a', 'b');
for($i=0; $i<3; $i++){
if(isset($b[$i])){
$a += array($b[$i] => array(1, 2, 3));
}
}
DEMO.
You can set the key for $a like so:
$a = array();
$b = array('a', 'b');
for($i=0; $i<count($b); $i++){
$a[$b[$i]] = array(1, 2, 3);
}
print_r($a);
Also, i changed your for loop to use count($b) as you where iterating 1 to many times with your hard coded 3
Try:
$a[$b[$i]] = array(1,2,3);
One more iteration..
$a = array();
$b = array('a', 'b');
for($i=0; $i<3; $i++){
$a[$b[$i]] = array(1, 2, 3);
}
print_r($a);
Let's check what you were doing wrong.
$a = array();
$b = array('a', 'b'); // Count of elements is 2
for($i=0; $i<3; $i++){ // this will loop 3 times assigning 0,1,2 to $i where. You only needed 0 and 1 for an array with 2 elements
$a[] = array($b[$i] => array(1, 2, 3)); // here you are adding a new element to $a without providing key. So it becomes a numeric indexed array.
}
Solution:
for($i=0; $i<count($b); $i++){ // you could use $i<2 as well however count($b) makes your code more dynamic and result won't be affected if no of elements in $b changes.
$a[$b[$i]] =array(1, 2, 3); // you put $b[$i] as key for $a which creates an associative array
}
So I'm using the pin method, but the reference is detected one level too late:
$pin = time();
function wrap($arr){
test($arr);
}
function test(&$arr){
global $pin;
if(in_array($pin, $arr))
return print "ref";
$arr[] = $pin;
foreach($arr as &$v){
if($v != $pin){
if(is_array($v))
return test($v);
print $v . " ";
}
}
}
$array = array(1, 2, 3);
$array[4] = &$array;
wrap($array);
I get 1 2 3 1 2 3 rec
But I expect 1 2 3 rec
If I just do test($arr) then it works, but the problem is that I need to wrap the test function inside another one that accepts values not references :(
Is there any way I can detect the reference at the right moment with my wrapper function too?
Introduction
I think a better approach would be to create a copy of the array and compare modification rather than use global pin and it can still be a 100% Recursive
Example 1
This is from your example above :
$array = array(1,2,3);
$array[4] = &$array;
wrap($array);
Output
Array
(
[0] => 1
[1] => 2
[2] => 3
[4] => ref
)
Example 2
Are we really sure its detecting reference or just a copy of the array
//Case 1 : Expect no modification
$array = array(1, 2, 3, array(1, 2, 3));
wrap( $array);
//Case 2 : Expect Modification in Key 2
$array = array(1, 2, 3, array(1, 2, 3));
$array[2] = &$array;
wrap( $array);
Output
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
)
Array
(
[0] => 1
[1] => 2
[2] => ref
[3] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
)
Example 3
Is this really recursive ?
$array = array(1, 2, 3, array(1, 2, 3));
$array[4][4][2][6][1] = array(1,2,3=>&$array);
wrap( $array);
Output
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[4] => Array
(
[4] => Array
(
[2] => Array
(
[6] => Array
(
[1] => Array
(
[0] => 1
[1] => 2
[3] => ref <-- GOT YOU
)
)
)
)
)
)
Your Modified Function
/**
* Added printf since test now returns array
* #param array $arr
*/
function wrap(array $arr) {
printf("<pre>%s<pre>", print_r(test($arr), true));
}
/**
* - Removed Top Refrence
* - Removed Global
* - Add Recursion
* - Returns array
* #param array $arr
* #return array
*/
function test(array $arr) {
$temp = $arr;
foreach ( $arr as $key => &$v ) {
if (is_array($v)) {
$temp[$key]['_PIN_'] = true;
$v = isset($arr[$key]['_PIN_']) ? "ref" : test($v);
}
}
unset($temp); // cleanup
return $arr;
}
I think you are over-complicating things. I solved this by looping over the array and checking if the current value in the array is equivalent (===) with the array.
function wrap( $arr){
test($arr);
}
function test( $arr){
foreach( $arr as $v) {
if( $v === $arr) {
print 'ref, ';
} else {
if( is_array( $v)) {
test( $v);
} else {
print $v . ', ';
}
}
}
}
I used the following test cases:
echo "Array 1:\n";
$array1 = array(1, 2, 3);
$array1[4] = &$array1;
wrap( $array1);
echo "\nArray 2:\n";
$array2 = array(1, 2, 3, array(1, 2, 3));
$array2[2] = &$array2;
wrap( $array2);
Which produced this output:
Array 1:
1, 2, 3, ref
Array 2:
1, 2, ref, 1, 2, 3,
However, the above method will fail for nested references. If nested references are possible, as in the following test case:
echo "\nArray 3:\n";
$array3 = array(1, 2, 3, array(1, 2, 3));
$array3[3][2] = &$array3;
wrap( $array3);
Then we need to keep track of all the array references we've seen, like this:
function wrap( $arr){
test( $arr);
}
function test( $arr){
$refs = array(); // Array of references that we've seen
$f = function( $arr) use( &$refs, &$f) {
$refs[] = $arr;
foreach( $arr as $v) {
if( in_array( $v, $refs)) {
print 'ref, ';
} else {
if( is_array( $v)) {
$f( $v);
} else {
print $v . ', ';
}
}
}
};
$f( $arr);
}
Using the above test case, this outputs:
Array 3:
1, 2, 3, 1, ref, 3,
Edit: I've updated the final function that keeps track of all references to eliminate the global dependencies.
function wrap($arr){ test($arr); }
/// ...
wrap($array);
Your wrap() function allocates new memory block for $arr. When you calling test() function within wrap()s body, it takes reference of $arr memory block, but not an $arrays memory block, because $arr is a copy of $array and PHP memory management system stores them separately.
There is a universal reference spotting function:
function is_equal_refs(&$a, &$b){
$buffer = $a; // saving current value in temporary variable
$a = md5(time()); // assigning new value to memory block, pointed by reference
$result = ($a === $b); // if they're still equal, then they're point to the same place.
$a = $buffer; // restoring value
return $result; // returning result
}
So, lets do some testing:
<?php
header('Content-Type: text/plain');
function is_equal_refs(&$a, &$b){
$buffer = $a;
$a = md5(time());
$result = ($a === $b);
$a = $buffer;
return $result;
}
function wrap($arr){ test($arr); }
function test(&$arr){
foreach($arr as &$v){
if(is_equal_refs($arr, $v)){
print_r('ref');
echo PHP_EOL;
break;
}
if(is_array($v))return test($v);
print_r($v);
echo PHP_EOL;
}
}
$array = array(1, 2, 3);
$array[] = &$array;
wrap($array);
?>
Shows:
1 // < $arr
2
3
1 // < $array
2
3
ref // < $array doubled -> reference found
The reason of such behavior is $arr[3] contains reference for $arrays memory block, but not reference of itself's memory block.
Lets remove a $array[] = &$array; row, and modify wrap() function to check:
function wrap($arr){
$arr[] = &$arr;
test($arr);
}
And result would be:
1 // < $arr
2
3
ref // < $arr doubled -> reference found
Because $arr not points to $array, but to itself in $arr[3]. So, in your code there are different references you want to spot.
CONCLUSION: What you want to achieve is breaking out PHP memory management rules.
UPDv1:
Need to seek a workaround, to restore $array reference in a wrap() function scope.
1) A "bad" / "globals" practice:
<?php
header('Content-Type: text/plain');
function is_equal_refs(&$a, &$b){
$buffer = $a;
$a = md5(time());
$result = ($a === $b);
$a = $buffer;
return $result;
}
function wrap($array){
global $check; // <- THIS
test(empty($check) ? $array : $check); // <- THIS
}
function test(&$arr){
foreach($arr as &$v){
if(is_equal_refs($v, $arr)){
print_r('ref');
echo PHP_EOL;
break;
}
if(is_array($v)){
test($v);
} else {
print $v . ' ';
echo PHP_EOL;
}
}
}
$array = array(1, 2, 3);
$array[] = &$array;
$check = &$array; // <- and THIS
wrap($array);
?>
Which shows:
1
2
3
ref
2) A "wrap everything in array or object" practice: (prefered and reliable)
<?php
header('Content-Type: text/plain');
define('REF_MARKER', 'x-my-tr!cky-ref'); // trick key definition
function is_equal_refs(&$a, &$b){
$buffer = $a;
$a = md5(time());
$result = ($a === $b);
$a = $buffer;
return $result;
}
function wrap(array $arr){
// restore reference, if trick.
// it might be moved to the top part of test() function (might affect performance).
if(isset($arr[REF_MARKER]))$arr = &$arr[REF_MARKER];
test($arr);
}
// $array - subject to test;
// $refs - internal ref list of all `subjects`;
function test(&$array, $refs = array()){
$refs[] = &$array;
foreach($array as &$value){
foreach($refs as &$ref){
if(is_equal_refs($ref, $value))return print 'ref ';
}
if(is_array($value)){
$refs[] = &$value;
test($value, $refs);
} else {
print $value . ' ';
}
}
}
$array = array(1, 2, 3);
$array[] = &$array;
wrap(array(REF_MARKER => &$array)); // trick
print PHP_EOL;
$ring = array(1, 2, 3, array(4, 5, 6));
$ring[3][] = &$ring;
wrap(array(REF_MARKER => &$ring)); // trick
print PHP_EOL;
$test = array('a', 'b', 'c');
$ring = array(1, 2, 3);
$ring[] = &$test;
$test[] = &$ring;
wrap(array(REF_MARKER => &$ring)); // trick
print PHP_EOL;
wrap(range(1, 5)); // normal
print PHP_EOL;
$test = array(1, 2, 3, array(1, 2, 3), 4, array(5, 2, 3), array(6, array(1, 2, 3), 7), array(1, 2, 3));
wrap($test); // normal
print PHP_EOL;
$test[] = &$test;
$test[3][] = &$test;
$test[5][] = &$test[3];
wrap(array(REF_MARKER => &$test)); // trick
?>
Shows:
1 2 3 ref
1 2 3 4 5 6 ref
1 2 3 a b c ref
1 2 3 4 5
1 2 3 1 2 3 4 5 2 3 6 1 2 3 7 1 2 3
1 2 3 1 2 3 ref 4 5 2 3 ref 6 1 2 3 7 1 2 3 ref
I have two multidimensional arrays that I need to determine the delta for each value. I know the array_diff function only returns the difference in keys. Is there a functon that will determine the delta for each set of values assuming the two arrays contain the same set of keys?
Example:
array_1(test1 => Array([key1] => 100, [key2] => 200 ) )
array_2(test1 => Array([key1] => 105, [key2] => 195 ) )
I would expect something like:
array_3(test1 => Array([key1] => 5, [key2] => -5 ) )
Are there any PHP methods to do this or am I on my own?
Answers here suggested using foreach loop but I think creating anonymous function will be easier:
<?php
$count_delta = create_function('$a,$b', 'return $a - $b;');
$arr1 = array(100, 200);
$arr2 = array(20, 180);
$delta = array_map($count_delta, $arr1, $arr2);
var_dump($delta);
Output will be:
array
0 => int 80
1 => int 20
$delta = array();
foreach( $array1 as $k=>$v )
{
if( array_key_exists( $k, $array2 )
{
// preserve the key
$delta[$k] = $array1[$k] - $array2[$k];
// or don't
$delta[] = $array1[$k] - $array2[$k];
}
}
print_r($delta);
There is no built-in function for that, but you can use this.
function delta_array($a, $b) {
if (sizeof($a) != sizeof($b))
return false;
$arr = array();
for ($i=0; $i < $c = sizeof($a); $i++)
$arr[] = $b[$i] - $a[$i];
return $arr;
}
$arr1 = array(100,200);
$arr2 = array(105,195);
$delta = delta_array($arr1, $arr2);
print_r($delta);
The above will return
Array
(
[0] => -5
[1] => 5
)