Foreach with reference causing array's last element to repeat - php

I ran into a weird issue and could reproduce it with this snippet:
<?php
$arr = [];
for($i = 0; $i <= 3; $i++) {
$arr[] = [
$i
];
}
foreach ($arr as &$item) {
$item[] = $item[0];
}
foreach ($arr as $item) {
print_r($item);
}
It is outputting (notice the last element had been replaced with a copy of its previous one):
Array
(
[0] => 0
[1] => 0
)
Array
(
[0] => 1
[1] => 1
)
Array
(
[0] => 2
[1] => 2
)
Array
(
[0] => 2
[1] => 2
)
However, here's the expected result:
Array
(
[0] => 0
[1] => 0
)
Array
(
[0] => 1
[1] => 1
)
Array
(
[0] => 2
[1] => 2
)
Array
(
[0] => 3
[1] => 3
)
If I use array_map instead of the first foreach, it works:
<?php
$arr = [];
for($i = 0; $i <= 3; $i++) {
$arr[] = [
$i
];
}
$arr = array_map(function ($item) {
$item[] = $item[0];
return $item;
}, $arr);
foreach ($arr as $item) {
print_r($item);
}
Tested under PHP 8.0.0.
What could be causing this difference? Is there something about array pointers I'm missing?

In your first example, the $item variable still holds a reference to the last value of the $arr when the loop is done.
foreach ($arr as &$item) {
$item[] = $item[0];
}
If you use print_r(get_defined_vars()); you can see that there is a key in the array with name item
The behaviour of the double value at the end of the foreach is described here and It is recommended to destroy the specified variables using unset($item)
You can also use a different variable name.
Using array_map, the callback gets a function argument passed by name, which is bound to the function scope.
If you run print_r(get_defined_vars()); after using array_map you can see that there is no key named item

$arr = [];
for($i = 0; $i <= 3; $i++) {
$arr[] = [
$i
];
}
/** Output
* $arr = [[0],[1],[2],[3]]
*/
In this foreach loop you are just selecting the existing value of item and setting it into a new index of referenced item
foreach ($arr as &$item) {
$item[] = $item[0];
}
Type of $item is array, $item[0] is referred to its 0 index
$item[] means a new array index inside item that is currently referenced to.

Related

Create 2-element rows from flat array where the every second row value is also the first value of the next row

$arrayinput = array("a", "b", "c", "d", "e");
How can I achieve the following output....
output:
Array
(
[0] => Array
(
[0] => a
[1] => b
)
[1] => Array
(
[0] => b
[1] => c
)
[2] => Array
(
[0] => c
[1] => d
)
[3] => Array
(
[0] => d
[1] => e
)
[4] => Array
(
[0] => e
)
)
You can use this, live demo here.
<?php
$arrayinput = array("a","b","c","d","e");
$array = [];
foreach($arrayinput as $v)
{
$arr = [];
$arr[] = $v;
if($next = next($arrayinput))
$arr[] = $next;
$array[] = $arr;
}
print_r($array);
live example here: http://sandbox.onlinephpfunctions.com/code/4de9dda457de92abdee6b4aec83b3ccff680334e
$arrayinput = array("a","b","c","d","e");
$result = [];
for ($x = 0; $x < count($arrayinput); $x+=2 ) {
$tmp = [];
$tmp[] = $arrayinput[$x];
if ($x+1 < count($arrayinput)) $tmp[] = $arrayinput[$x+1];
$result[] = $tmp;
}
var_dump($result);
By declaring single-use reference variables, you don't need to call next() -- which is not falsey-safe and there is a warning in the php manual. You also won't need to keep track of the previous index or make iterated calls of count().
For all iterations except for the first one (because there is no previous value), push the current value into the previous subarray as the second element. After pushing the second value, "disconnect" the reference variable so that the same process can be repeated on subsequent iterations. This is how I'd do it in my own project.
Code: (Demo)
$result = [];
foreach (range('a', 'e') as $value) {
if ($result) {
$row[] = $value;
unset($row);
}
$row = [$value];
$result[] = &$row;
}
var_export($result);
If you always want to have 2 elements in each subarray and you want to pad with null on the final iteration, you could use a transposing technique and send a copy of the input array (less the first element) as an additional argument. This is a much more concise technique (one-liner) (Demo)
var_export(array_map(null, $array, array_slice($array, 1)));

Get only Numeric values from Array in PHP

I have an array that looks something like this:
Array (
[0] => Array ( [country_percentage] => 5 %North America )
[1] => Array ( [country_percentage] => 0 %Latin America )
)
I want only numeric values from above array. I want my final array like this
Array (
[0] => Array ( [country_percentage] => 5)
[1] => Array ( [country_percentage] => 0)
)
How I achieve this using PHP?? Thanks in advance...
When the number is in first position you can int cast it like so:
$newArray = [];
foreach($array => $value) {
$newArray[] = (int)$value;
}
I guess you can loop the 2 dimensional array and use a preg_replace, i.e.:
for($i=0; $i < count($arrays); $i++){
$arrays[$i]['country_percentage'] = preg_replace( '/[^\d]/', '', $arrays[$i]['country_percentage'] );
}
Ideone Demo
Update Based on your comment:
for($i=0; $i < count($arrays); $i++){
if( preg_match( '/North America/', $arrays[$i]['country_percentage'] )){
echo preg_replace( '/[^\d]/', '', $arrays[$i]['country_percentage'] );
}
}
Try this:
$arr = array(array('country_percentage' => '5 %North America'),array("country_percentage"=>"0 %Latin America"));
$result = array();
foreach($arr as $array) {
$int = filter_var($array['country_percentage'], FILTER_SANITIZE_NUMBER_INT);
$result[] = array('country_percentage' => $int);
}
Try this one:-
$arr =[['country_percentage' => '5 %North America'],
['country_percentage' => '0 %Latin America']];
$res = [];
foreach ($arr as $key => $val) {
$res[]['country_percentage'] = (int)$val['country_percentage'];
}
echo '<pre>'; print_r($res);
output:-
Array
(
[0] => Array
(
[country_percentage] => 5
)
[1] => Array
(
[country_percentage] => 0
)
)
You can use array_walk_recursive to do away with the loop,
passing the first parameter of the callback as a reference to modify the initial array value.
Then just apply either filter_var or intval as already mentioned the other answers.
$array = [
["country_percentage" => "5 %North America"],
["country_percentage" => "0 %Latin America"]
];
array_walk_recursive($array, function(&$value,$key){
$value = filter_var($value,FILTER_SANITIZE_NUMBER_INT);
// or
$value = intval($value);
});
print_r($array);
Will output
Array
(
[0] => Array
(
[country_percentage] => 5
)
[1] => Array
(
[country_percentage] => 0
)
)
You could get all nemeric values by looping through the array. However I don't think this is the most efficient and good looking answer, I'll post it anyways.
// Array to hold just the numbers
$newArray = array();
// Loop through array
foreach ($array as $key => $value) {
// Check if the value is numeric
if (is_numeric($value)) {
$newArray[$key] = $value;
}
}
I missunderstood your question.
$newArray = array();
foreach ($array as $key => $value) {
foreach ($value as $subkey => $subvalue) {
$subvalue = trim(current(explode('%', $subvalue)));
$newArray[$key] = array($subkey => $subvalue);
}
}
If you want all but numeric values :
$array[] = array("country_percentage"=>"5 %North America");
$array[] = array("country_percentage"=>"3 %Latin America");
$newArray = [];
foreach ($array as $arr){
foreach($arr as $key1=>$arr1) {
$newArray[][$key1] = intval($arr1);
}
}
echo "<pre>";
print_R($newArray);
This is kind of a ghetto method to doing it cause I love using not as many pre made functions as possible. But this should work for you :D
$array = array('jack', 2, 5, 'gday!');
$new = array();
foreach ($array as $item) {
// IF Is numeric (each item from the array) will insert into new array called $new.
if (is_numeric($item)) { array_push($new, $item); }
}

Concat values of array with same key

I want to concat values of array with same key
Example:
[0] => Array
(
[0] => A
[1] => XYZ
)
[1] => Array
(
[0] => B
[1] => ABC
)
[2] => Array
(
[0] => A
[1] => LMN
)
[3] => Array
(
[0] => B
[1] => PQR
)
)
Expected output:
[0] => Array
(
[0] => A
[1] => XYZ,LMN
)
[1] => Array
(
[0] => B
[1] => ABC,PQR
)
)
A simple solution uses the PHP function array_reduce():
// The input array you posted in the question
$input = array(
array('A', 'XYZ'),
array('B', 'ABC'),
array('A', 'LMN'),
array('B', 'PQR'),
);
// Reduce the array to a new array that contains the data aggregated as you need
$output = array_reduce(
// Process each $item from $input using a callback function
$input,
// The callback function processes $item; the partial result is $carry
function (array $carry, array $item) {
// Extract the key into a variable
$key = $item[0];
// If the key was encountered before
// then a partial entry already exists in $carry
if (isset($carry[$key])) {
// Append the desired value to the existing entry
$carry[$key][1] .= ','.$item[1];
} else {
// Create a new entry in $carry (copy $item to key $key for quick retrieval)
$carry[$key] = $item;
}
// Return the updated $carry
return $carry;
},
// Start with an empty array (it is known as $carry in the callback function)
array()
);
// $output contains the array you need
Try this:
$final = array();
foreach ($array_items as $item)
{
$key = $item[0];
$found_index = -1;
for ($i=0; $i<count($final); $i++)
{
if ($key == $final[$i][0])
{
$found_index = $i;
break;
}
}
if ($found_index == -1)
{
$final_item = array();
$final_item[0] = $key;
$final_item[1] = $item[1];
$final[] = $final_item;
}
else
{
$final[$found_index][1] .= ",".$item[1];
}
}
We create a new array $final, and loop through your old array $array_items. For each item, we see if there is already an item in $final that has the same [0] index. If it doesn't exist, we create it and add the initial string to the [1] index. If it does exist, we just have to add the string onto the end of the [1] index.
Try it, substituting $array_items for whatever your array is called, let me know if it works.
Check my solution. It should work fine. I hope it will help you much.
$result = $passed_keys = $extended_arr = [];
foreach ($arr as $k => $value) {
for($i = $k + 1; $i < count($arr); $i++){
if ( $value[0] == $arr[$i][0] ){ // compare each array with rest subsequent arrays
$key_name = $value[0];
if (!array_key_exists($key_name, $result)){
$result[$key_name] = $value[1] .",". $arr[$i][1];
} else {
if (!in_array($i, $passed_keys[$key_name])) {
$result[$key_name] .= ",". $arr[$i][1];
}
}
$passed_keys[$key_name][] = $i; // memorizing keys that were passed
}
}
}
array_walk($result, function($v, $k) use(&$extended_arr){
$extended_arr[] = [$k, $v];
});
The result is in $extended_arr
My solution, creates a custom key which makes identifying the letter much easier. This removes the need to continuously iterate through each array, which can become a major resources hog.
<?php
$inital_array = array(
array('A','XYZ'),
array('B','ABC'),
array('A','LMN'),
array('B','PQR')
);
$concat_array = array();
foreach($inital_array as $a){
$key = $a[0];
if( !isset($concat_array[$key]) ){
$concat_array[$key] = array($key,'');
}
$concat_array[$key][1] .= (empty($concat_array[$key][1]) ? '' : ',').$a[1];
}
$concat_array = array_values($concat_array);
echo '<pre>',print_r($concat_array),'</pre>';

manipulate a 2D array in PHP

I have a 2 Dimentional array in php as follow :
Array
(
[0] => Array
(
[0] => 10
[1] =>
)
[1] => Array
(
[0] => 67
[1] =>
)
[2] => Array
(
[0] => 67
[1] => 50
)
)
I want to manipulate it as follow:
Array
(
[0] => Array
(
[0] => 10
[1] => 67
[2] => 67
)
[1] => Array
(
[0] =>
[1] =>
[2] => 50
)
)
Means I want to take first elements of all inner arrays in one array and 2nd element in another array.
How can I manipulate this. Plz help
You can array_map() instead of loop. Example:
$newArr[] = array_map(function($v){return $v[0];},$arr);
$newArr[] = array_map(function($v){return $v[1];},$arr);
Or can use array_column() if your PHP 5.5+
$newArr[] = array_column($arr, 0);
$newArr[] = array_column($arr, 1);
print '<pre>';
print_r($newArr);
print '</pre>';
Just run the following script:
$array1 = array();
for ($i = 0; $i < count($array1); $i++) {
for ($j = 0; $j < count($array1[$i]); $j++) {
$array2[$j][$i] = $array1[$i][$j];
}
}
Here's a general solution that works regardless of how many items you have in the array and the sub-arrays:
// Set up an array for testing
$my_array = array( array(10, null), array(67, null), array(67, 50));
/**
* Our magic function; takes an array and produces a consolidated array like you requested
* #param array $data The unprocessed data
* #return array
*/
function consolidate_sub_arrays($data)
{
/**
* The return array
* #var array $return_array
*/
$return_array = array();
// Loop over the existing array
foreach ($data as $outer) {
// Loop over the inner arrays (sub-arrays)
foreach($outer as $key => $val) {
// Set up a new sub-array in the return array, if it doesn't exist
if (!array_key_exists($key, $return_array)) {
$return_array[$key] = array();
}
// Add the value to the appropriate sub-array of the return array
$return_array[$key][] = $val;
}
}
// Done!
return $return_array;
}
// Just to verify it works; delete this in production
print_r(consolidate_sub_arrays($my_array));
You need to loop over the initial array and create a new array in the format you want it.
$new_array = array();
foreach ($input_array as $in ) {
$new_array[0][] = $in[0];
$new_array[1][] = $in[1];
}
print_r($new_array);

php array conversion help needed

$cnt[0]=>Array( [0] => 0 [1] => 0 [2] => 0 ),
$cnt[1] => Array ( [0] => 1 [1] => 0 [2] => 0 )
i want convert this array to below result,
$cnt[0]=(0,0);
$cnt[1]=(0,0);
$cnt[2]=(0,1);
any php function there to convert like this format,
Thanks,
Nithish.
function flip($arr)
{
$out = array();
foreach ($arr as $key => $subarr)
{
foreach ($subarr as $subkey => $subvalue)
{
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
see more example in
PHP - how to flip the rows and columns of a 2D array
I'm interpreting your expected output to be something like a list of pairs:
$pairs = array(
array(1,0),
array(0,0),
array(0,0)
);
You'd simply check that the sub-arrays are the same length, and then use a for loop:
assert('count($cnt[0]) == count($cnt[1])');
$pairs = array();
for ($i = 0; $i < count($cnt[0]); ++$i)
$pairs[] = array($cnt[0][$i], $cnt[1][$i]);

Categories