Alternative to Variable Variables in PHP - php

In this loop, i am iterating through an array and performing an API call each time. This works fine but I keep reading that using variable variables is not good practice. How could I rewrite this code without using them?
edit: I am not using an array because I have to pass the variables into another template along has other variables outside that array.
template( 'template-name', [ 'graphOne' => $graphOne, 'graphTwo' => $graphTwo, 'outsideVar' => $anothervalue ] );
<?php
// Array of categories for each graph
$catArray = [
'One' => '3791741',
'Two' => '3791748',
'Three' => '3791742',
'Four' => '3791748'
];
foreach ( $catArray as $graphNum => $cat )
{
// Hit API
$graph_results = theme( 'bwatch_graph_call', [
'project' => '153821205',
'category' => $cat
]
);
${"graph{$graphNum}"} = $graph_results;
// Outputs $graphOne, $graphTwo, $graphThree...
}
// Pass vars to template
template( 'template-name', [
'graphOne' => $graphOne,
'graphTwo' => $graphTwo,
'outsideVar' => $anothervalue ]
);

You can use PHP's array_merge when you make your template() call if you have multiple arrays with different key=>value pairs.
http://php.net/array_merge
Here is an example if you have multiple arrays (key=>value pairs) you want to pass to the template.
// Array of categories for each graph
$catArray = [
'One' => '3791741',
'Two' => '3791748',
'Three' => '3791742',
'Four' => '3791748'
];
// Array of category results
$catResult = [];
foreach ( $catArray as $graphNum => $cat )
{
// Hit API
$catResult['graph' . $graphNum] = theme( 'bwatch_graph_call', [
'project' => '153821205',
'category' => $cat
]
);
}
// Now you have an array of results like...
// $catResult['graphOne'] = 'result for One';
// $catResult['graphTwo'] = 'result for Two';
$otherArray1 = ['outsideVar' => $anothervalue];
$otherArray2 = ['somethingElse' => $oneMoreValue];
// Pass all arrays as one
template('template-name', array_merge($catResult, $otherArray1, $otherArray2));

Related

Group rows of data by subarray in column and create subsets with variable depth

I have an array with two items, which are also arrays themselves: product and countries.
There are cases in which the countries array is the same for more than one product, like basic and pro in the example below.
Given this array:
$array = [
[
'product' => [
'value' => 'basic',
'label' => 'Basic'
],
'countries' => [
'Japan', // these
'Korea' // two...
],
],
[
'product' => [
'value' => 'pro',
'label' => 'Pro'
],
'countries' => [
'Japan', // ...and these two
'Korea' // are identical...
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
I would like to create new arrays grouped by countries, more precisely,
this is the result I'm after:
$array = [
[
'product' => [
[
'value' => 'basic',
'label' => 'Basic'
],
[
'value' => 'pro',
'label' => 'Pro'
]
],
'countries' => [
'Japan', // ...so they are now one single array
'Korea' // as the two products 'basic' and 'pro' have been grouped
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
As you can see in the second snippet, what I'm trying to do is to group basic and pro together in the same array, since they both share the exact same countries (Korea and Japan).
I've been trying for days to play around with this code, but it only seems to work if product and countries are strings rather than arrays:
$grouped = array();
foreach ($array as $element) {
$grouped[$element['countries']][] = $element;
}
var_dump($grouped);
This might be what you want
$productsByCountrySet = [];
foreach ($array as $product) {
$countries = $product['countries'];
sort($countries);
$countrySet = implode('/', $countries);
if (isset($productsByCountrySet[$countrySet])) {
$productsByCountrySet[$countrySet]['product'][] = $product['product'];
} else {
$productsByCountrySet[$countrySet] = [
'product' => [$product['product']],
'countries' => $countries,
];
}
}
$products = [];
foreach ($productsByCountrySet as $p) {
if (count($p['product']) == 1) {
$p['product'] = $p['product'][0];
}
$products[] = $p;
}
print_r($products);
It produces the output you're aiming for. It assumes that the order of countries is not significant (ie ['Japan', 'Korea'] is the same as ['Korea', 'Japan'])
It works by turning your countries array into a string (['Japan', 'Korea'] becomes 'Japan/Korea'), then uses that as a unique key for the entries. It builds up the desired output array by first assembling the unique key (I called it 'country set') and then checking if it has already been seen. If it has, the product is appended, if not, a new item is added to the output array.
The final section handles the case where there is only one product for a country set. We loop and catch this state, modifying the output accordingly.
I personally would not build the result structure that you are seeking because it would make the array processing code more convoluted, but hey, it's your project.
You need to establish consistent, first-level string keys in your result array so that you can determine if a set of countries has been encountered before.
If never encountered, save the full row data to the group.
If encountered, specifically, for a second time, you need to restructure the group's data (this is the elseif() logic).
If encountered more than twice, you can safely push the product's row data as a new child of the deeper structure.
Code: (Demo)
$result = [];
foreach ($array as $row) {
sort($row['countries']);
$compositeKey = implode('_', $row['countries']);
if (!isset($result[$compositeKey])) {
$result[$compositeKey] = $row;
} elseif (isset($result[$compositeKey]['product']['value'])) {
$result[$compositeKey]['product'] = [
$result[$compositeKey]['product'],
$row['product']
];
} else {
$result[$compositeKey]['product'][] = $row['product'];
}
}
echo json_encode(array_values($result), JSON_PRETTY_PRINT);
This general approach is efficient and direct because it only makes one pass over the array of data.
See my related answer: Group array row on one column and form subarrays of varying depth/structure
<?php
$array = [
[
'product' => [
'value' => 'basic',
'label' => 'Basic'
],
'countries' => [
'Japan', // these
'Korea' // two...
],
],
[
'product' => [
'value' => 'pro',
'label' => 'Pro'
],
'countries' => [
'Japan', // ...and these two
'Korea' // are identical...
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
// print(serialize($array));
$newarr = [];
//Here I am sorting the countries so that it can be compared and making a new array
foreach ($array as $key) {
$new = $key['countries'];
sort($key['countries']);
sort($key['product']);
$newarr[] = $key;
}
$result = [];
foreach($newarr as $key => $value) {
//Genetraing a unique key for each array type so that it can be compared
$ckey = md5(serialize($value['countries']));
$pkey = md5(serialize($value['product']));
//In the new array, the unique Countries key is used to generate a new array which will contain the product & countries
$result[$ckey]['product'][$pkey] = $value['product'];
//Product key is used to reduce redunant entires in product array
$result[$ckey]['countries'] = $value['countries'];
//This new loop is used to compare other arrays and group them together
foreach($newarr as $key2 => $value2) {
if($key != $key2 && $value['countries'] == $value2['countries']) {
$result[$ckey]['product'][$pkey] = $value2['product'];
}
}
}
print_r($result);
And the output is
Array
(
[00a9d5d0be04135916148f84706a2073] => Array
(
[product] => Array
(
[1c24c036cffc896aebf291da101ff88d] => Array
(
[0] => Pro
[1] => pro
)
[712ef34513bad5c6dd490337c22a5807] => Array
(
[0] => Basic
[1] => basic
)
)
[countries] => Array
(
[0] => Japan
[1] => Korea
)
)
[ae57f65be4cd65148d6f4ed3def12c8f] => Array
(
[product] => Array
(
[be5b95a64169e073ed0b6a72dfb79a83] => Array
(
[0] => Expert
[1] => expert
)
)
[countries] => Array
(
[0] => France
[1] => Japan
)
)
)
This way is little hacky and not the fastest solution but a working one. Since you don't have to do complex array operations it has unique keys rather than an index, that makes the process easy.l
Please read the code comments, I said how it works.
I have a solution in which I create a new array for the result called $newArray and I put the first element from $array into it. I then loop through each element in $array (except for the first one which I exclude using its key). For each element in $array, I loop through each element in $newArray. If both country names are present in $newArray, I just add the product array to $newArray. If there is no element in $newArray with both countries from the $array element being considered then I add the full $array element to $newArray. It does give your required array given your input array.
I had to change the way the product array appears in $newArray which explains the second and third lines of code below.
The & in &$subNewArr has the effect that $subNewArr is 'passed by reference' which means that it can be altered by the code where it is being used (see https://www.php.net/manual/en/language.types.array.php).
$newArray = [$array[0]];
$newArray[0]['product'] = [];
$newArray[0]['product'][] = $array[0]['product'];
foreach($array as $key => $subArr){
if($key > 0){
foreach($newArray as &$subNewArr){
if(
in_array($subArr['countries'][0], $subNewArr['countries']) &&
in_array($subArr['countries'][1], $subNewArr['countries'])
){
array_push($subNewArr['product'], $subArr['product']);
continue 2;
}
}
$newArray[] = $subArr;
}
}

Adding data to array through loop php

I have a problem where I am getting data from database and need to put that in an array. The array is associative and I am not sure about this practice so I thought I should ask the community. Is this the correct way of adding data to array? The array is for the radio buttons that will be provided to the helper class in prestashop. The array structure is important. This is the var_dump array structure which I have in $options_break2.
$options_value = array();
$options = array();
for($z=0; $z<sizeof($options_break2); $z++)
{
$options_value = array_push($options_value,
array(
"id" => $options_break2[$z],
"name" => $options_break2[$z],
"label" => $options_break2[$z],
)
);
}
$options = array_push($options, $options_value);
What I want is that the array should contain something like:
$example = array(
array(
'id_option' => 'some value',
'name' => 'some value',
),
array(
'id_option' => 'some value',
'name' => 'some value',
),
);
Actually you don't need to use array_push and array() if your PHP version is above 5.6, and you can improve your loop by using the foreach loop:
$options_value = [];
foreach ($options_break2 as $opt) {
$options_value[] = [
"id_option" => $opt, // some_value
"name" => $opt // some_value
];
}
$options = $options_value; // you don't really need this

PHP create JSON with foreach

I have following PHP code, to create JSON with foreach out of an array:
$array = array('one', 'two', 'three', 'four');
foreach ($array as $key => $value) {
$temp = array(
'text' => 'Hello',
'text1' => 5,
'collect' => array(
$value => array(
'xx' => 'yy',
'key' => $key
)
)
);
echo json_encode($temp);
}
The Output is this:
{
"text":"Hello",
"text1":5,
"collect":{"one":{"xx":"yy","key":0}}
}
{
"text":"Hello",
"text1":5,
"collect":{"two":{"xx":"yy","key":1}}
}
{
"text":"Hello",
"text1":5,
"collect":{"three":{"xx":"yy","key":2}}
}
{
"text":"Hello",
"text1":5,
"collect":{"four":{"xx":"yy","key":3}}
}
But i want this:
{
"text":"Hello",
"text1":5,
"collect": {
"one":{"xx":"yy","key":0},
"two":{"xx":"yy","key":1},
"three":{"xx":"yy","key":2},
"four":{"xx":"yy","key":3}
}
}
I get single 4 single JSON Objects, but i need only one with an collect object.
I don't get it...
I'd like to educate readers on a couple alternative methods as well as highlight that the other two answers needlessly instantiate the collect subarray prior to the loop (Sahil's answer does this twice for some reason).
The initial input array and the static elements of the result array should be placed at the start as the other answers do. Purely due to personal preference, I'll be using short array syntax.
Inputs:
$array=['one','two','three','four'];
$result=['text'=>'Hello','text1'=>5]; // <-- no 'comment' element declared
Now for the different methods that traverse $array and build the dynamic elements of the result.
Method #1: array_walk()
array_walk($array,function($v,$k)use(&$result){
$result['collect'][$v]=['xx'=>'yy','key'=>$k];
});
Method #2: array_map()
array_map(function($k,$v)use(&$result){
$result['collect'][$v]=['xx'=>'yy','key'=>$k];
},array_keys($array),$array);
Array map is less efficient because it requires an additional array to be passed to the function.
Method #3: foreach()
foreach($array as $k=>$v){
$result['collect'][$v]=['xx'=>'yy','key'=>$k];
}
$result at this point looks like this:
array (
'text' => 'Hello',
'text1' => 5,
'collect' => array (
'one' => array ( 'xx' => 'yy', 'key' => 0 ),
'two' => array ( 'xx' => 'yy', 'key' => 1 ),
'three' => array ( 'xx' => 'yy', 'key' => 2 ),
'four' => array ( 'xx' => 'yy', 'key' => 3 )
)
)
foreach() is the simplest and easiest to read for this case, but it important to understand and compare versus php's array functions to ensure you are using the best method for any given project.
For anyone wondering what the & is doing in use(&$result), that is a reference which is used in the anonymous function (aka closure) to make the $result variable "modifiable" within the function.
Finally convert to json using json_encode() and display with echo:
echo json_encode($result);
All of the above methods product the same desired output:
{"text":"Hello","text1":5,"collect":{"one":{"xx":"yy","key":0},"two":{"xx":"yy","key":1},"three":{"xx":"yy","key":2},"four":{"xx":"yy","key":3}}}
Here is the Demo of all three methods
Try this simple code ,in which we have a declaration before foreach.
Try this code snippet here
<?php
ini_set('display_errors', 1);
$array = array('one', 'two', 'three', 'four');
$temp = array(
'text' => 'Hello',
'text1' => 5,
'collect' => array()
);
$collect = array();
foreach ($array as $key => $value)
{
$collect[$value] = array(
'xx' => 'yy',
'key' => $key
);
}
$temp["collect"]=$collect;
echo json_encode($temp);
Output:
{
"text": "Hello",
"text1": 5,
"collect": {
"one": {
"xx": "yy",
"key": 0
},
"two": {
"xx": "yy",
"key": 1
},
"three": {
"xx": "yy",
"key": 2
},
"four": {
"xx": "yy",
"key": 3
}
}
}
You need to loop through and append these value arrays to the 'collect' key of your temp array.
$array = array('one', 'two', 'three', 'four');
$temp = array(
'text' => 'Hello',
'text1' => 5,
'collect' => array()
);
foreach ($array as $key => $value) {
$temp['collect'][$value] = array(
'xx' => 'yy',
'key' => $key
);
}
echo json_encode($temp);
Here is the demo: https://eval.in/788929

Access multidimensional array without multiple loops

Let's say I have an array formatted like so:
$data = array(
'variables' => array(
'823h9fhs9df38h4f8h' => array(
'name' => 'Foo',
'value' => 'green'
),
'sdfj93248fhfhf88rh' => array(
'name' => 'Bar',
'value' => 'red'
)
)
);
Say I wanted to access the name & values of each array in the variables array. Surely you can access it just looping over the main variables array and not looping over each individual item array? Something like so?
foreach ($data as $k => $v) {
$name = $data['variables'][0]['name'];
}
I'm sure I'm missing something simple...
You can do
foreach ($data['variables'] as $k => $v) {
$name = $v['name'];
}
You can also try this
create a new array containing just the names..
$new_arr = array_column($data['variables'],'name' );
echo $new_arr[0].'<br/>';
echo $new_arr[1].'<br/>';

multidimensional array - adding a key and value where a key and value is matched

I'm trying to add a key and value (associative) from an array to another array, where one specific key and value match. Here are the two arrays:
$array1 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2'
),
2 => array(
'walmart' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'milk' => 'product3'
)
);
$array2 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'bananas' => 'product3',
)
);
Here is the attempt I made at modifying $array1 to have key 'bananas' and value 'product3':
$dataCJ = getCJItem($isbn);
foreach ($array1 as $subKey => $subArray) {
foreach($subArray as $dkey => $dval){
foreach($array2 as $cjk => $cjv){
foreach($cjv as $cjkey => $cjval){
if($dval['walgreens'] == $cjval['walgreens']){
$dval['bananas'] = $cjval['bananas'];
}
}
}
}
}
This doesn't work. How can I fix this?
Change => $dval to => &$dval. Currently you are creating and writing to a new variable and the update will not work in-place.
I would look at array_merge() function!
Here is a start with the PHP doc.
For your specific case, you could do the following :
foreach($array1 as $key1 => $values1){
foreach($array2 as $key2 => $values2){
if($values1[0] == $values2[0]){
$array1[$key1] = array_merge($values1, $values2);
}
}
}
Note to simplify the problem you should inverse the first key=> value pair of the array.
Having an array this way would be a lot simper :
array(
'location' => "The location (eg:walgreens)",
//...
);
This way you could change the comparison to the following instead :
$values1['location'] == $values2['location']
Which would be safer in the case the array is not built with the location as the first pair.

Categories