How to rename the key in a foreach loop? - php

I'm trying to generate an XML from a database call, and the utility class I'm using throws an error Illegal character in tag name. tag: 0
I figured this is because of the array being [0],[1],[2] etc. Really I should set them all to be something standard, like 'asset' since they are all assets, but I can't do that because then keys will be identical, but maybe the ID would work. But I don't know how to change the key that I'm on within a foreach loop.
I tried:
foreach ($assets as &$key => &$asset) {
$key2 = $asset->isci;
$asset = get_object_vars($asset);
}
But I cannot pass reference for $key.
This is a brief representation of what the array looks like:
array(2) {
[0]=>
array(25) {
["id"]=>
string(2) "27"
}
[1]=>
array(25) {
["id"]=>
string(2) "25"
}
[2]=>
array(25) {
["id"]=>
string(1) "1"
}
}

Modifying keys of an array while looping over it is generally a bad idea - even if the language supported it, it can be very confusing to debug.
The simplest approach is to simply build a new array based on the old one, but with better keys:
$rekeyed_assets = array();
foreach ($assets as $old_key => $asset)
{
$new_key = $asset->isci;
$asset = get_object_vars($asset);
$rekeyed_assets[ $new_key ] = $asset;
}

The most convenient way is to change all the keys and then use array_combine to create a new array with the modified keys. Here's how you can do that in bulk using array_map and an anonymous function:
$newKeys = array_map(function($asset) { return $asset->isci; }, $assets);
$assets = array_combine($newKeys, $assets);
You can also do the same for the values:
$newKeys = array_map(function($asset) { return $asset->isci; }, $assets);
$newValues = array_map(function($asset) { return get_object_vars($asset); }, $assets);
$assets = array_combine($newKeys, $newValues);
This method does have a drawback (increased memory usage) which may be an issue if your array has millions of items, but in that case it's highly likely that it would be better to restructure the program so that it does not need to keep huge arrays in memory in the first place.

Related

Inefficient nested foreach loop

I have two array and want to merge it by using key of main array;
// $result : main dataset (multidimensional array)
// $referenceData : data related with main dataset and want to merge it into main dataset (multidimensional array)
if ($result) {
foreach ($result as $key => $val) {
foreach ($referenceData[$val['id']] as $refKey => $refVal) {
$result[$key][$refKey] = $refVal;
}
}
}
The thing is, when the result is high (even for 1000 elements on each) it takes more than 5-10 seconds which is quite unexpected for me.
I tried to use array_merge and array_merge_recursive instead of two foreach but I kept failing. Is there any way I could improve the logic? Thanks in advance.
Edit (added sample array);
result :
array(1) {
[0]=>
array(6) {
["id"]=>
string(5) "13020"
["name"]=>
string(23) "Data Stream 1"
["rank"]=>
string(1) "3"
["data_1"]=>
string(2) "63"
["data_2"]=>
string(2) "256"
["data_3"]=>
string(3) "469"
}
}
referenceData:
array(1) {
[13020]=>
array(5) {
["percent"]=>
float(20.987654320988)
["count_min"]=>
string(1) "1"
["count_max"]=>
int(2)
["checked"]=>
bool(false)
["cond_id"]=>
string(1) "0"
}
}
You need to make the first array structure exactly like the second array so that you can easily merge them. To do so you can do like below : (Its a reference code, you need to change it at your end)
$rows = $db->table('<table name>')->get()->getResultArray();
$result = [];
foreach ($rows as $row) {
$result[$row['id']] = $row;
}
Once both arrays have a similar structure, then you can go for a single foreach() and + operator to combine child arrays.
$finalArray = [];
foreach($result as $key=>$value){
$finalArray[$key] = $value+$referenceData[$key];
}
print_r($finalArray);
Output: https://3v4l.org/qBa4V
Note: if you want to re-index this new array (in case you want indexes like 0,1,2... so on) then you can do:
$finalArray = array_values($finalArray);

find if value exist in php into mysql built array

i build an array from mysql this way
$q="select account_code from chart_master;";
// Generate resultset
$result_set = $con->query($q);
$list = Array();
while( $myrow = mysqli_fetch_array($result_set) ) {
$list[] = $myrow;
}
when i dump $list i get:
array(79) { [0]=> array(2) { [0]=> string(8) "11011001" ["account_code"]=> string(8) "11011001" } [1]=> array(2) { [0]=> string(8) "11011002" ["account_code"]=> string(8) "11011002" } [2]=> array(2) { [0]=> string(8) "11011005" ["account_code"]=> string(8) "11011005" } ...
i now want to check if a value is found in the values 11011001, 11011002 etc with this code:
if (in_array($row['1'], $list))
{
echo $row['1']." found in the array";
}
with $row['1'] being one of the searched value.
I guess I am not looking at the right depth in the array because my in_array does not return anything.
Thoughts?
You should go through each element of your $list array and apply in_array to it.
foreach($list as $listItem){
if(in_array($row['1'], $listItem)){
echo $row['1']." found in the array";
}
}
in_array() only checks one dimension. That's why you need to go iterate through the first dimension and apply it to the second one.
The most succinct version of this I can imagine would be:
$exists = array_search('1001010101', array_column($list, 'account_code')) !== false;
This grabs the account_code column from the multidimensional array and looks inside that column for the value provided. If you only need an existence check, this seems to be a fast way of doing it.
However, if you don't need the rest of that SQL result set, I'd look into maybe doing a COUNT() or using a WHERE to narrow the result set instead. That would be more resource efficient.

foreach results in endless loop

I have an array stored in $_SESSION:
var_dump($_SESSION['session_article']);
//result:
array(2) {
[0]=> array(2) {
["id"]=> string(1) "3"
["amount"]=> int(2)
}
[1]=> array(2) {
["id"]=> string(2) "13"
["amount"]=> int(1)
}
}
If I do:
for($artKey = 0;$artKey < count($_SESSION['session_article']);$artKey++){
$cartArt = $_SESSION['session_article'][$artKey];
//stuff that doesn't affect key or value
}
everything is fine ...but if I do:
foreach($_SESSION['session_article'] as $artKey => $cartArt){
//stuff that doesn't affect key or value
}
the page won't stop to load (infinite loading, like the foreach never terminates)
I would like to you know that the computer is not a dumb machine, so whenever you do not get the right result then its not the computers problem, it is in how you code.
So lets take a look at your code first you var_dump($_SESSION['session_article']) you got an array with two elements each being an associative array. Now observe that this is an array of arrays so you code in scenario 2 is wrong.
If it were to be just a single array say $myArray = ['Simon', 'Peter', 'You'] then this woul have worked fine (note I used the short array syntax here you can use array() if you prefer). But the problem is you have multidimensional Arrays so you could should be
foreach($_SESSION['session_article'], list($a,))
{
//stuffs here
}
or better walk through the $_SESSION['session_article'] as an associative array like so
$myArray = $_SESSION['session_article']
$field = count($myArray, COUNT_RECURSIVE - (int)2)
for ($record = 0; $record < $myArray.length; $record++) {
//record number
for ($field = 0; $field < $field ; $field++) {
//combine record and field here
}
}
Hope I could lend a helping hand?
Please checkout
http://php.net/manual/en/function.count.php
http://php.net/manual/en/control-structures.foreach.php
They really have a good doc, just take you time.

advanced unset the value form array PHP

I have two arrays, what consist arrays. I need to merge these arrays recursive. But I need to do this action few times, and
array_merge_recursive()
will apend my data twice, I want to remove element what already exist in target array.
$messages array :
array(2) {
["messages"]=>
array(2) {
["test.testik"]=>
string(13) "Це тест"
["test2313.tes31231tik"]=>
string(23) "це тестончик"
}
["validators"]=>
array(4) {
["valid.validik"]=>
string(36) "Це валідне значення"
["joga.jimbo"]=>
string(27) "Джімбо торбінс"
["validka.invalidka"]=>
string(23) "це інвалідка"
["smith.john"]=>
string(17) "джон сміт"
}
}
$allCar array:
array(2) {
["messages"]=>
array(1) {
["test2313.tes31231tik"]=>
string(23) "це тестончик"
}
["validators"]=>
array(2) {
["validka.invalidka"]=>
string(23) "це інвалідка"
["smith.john"]=>
string(17) "джон сміт"
}
}
I wrote some code:
foreach ($messages as $domain => $messagesArray) {
foreach ($allCat as $d => $mess) {
if ($domain == $d) {
foreach ($messagesArray as $ymlkey => $trans) {
foreach ($mess as $ymlk => $transl) {
if ($ymlkey == $ymlk) {
unset($mess[$ymlk]);
}
}
}
}
}
}
Then when I run recursive merge it append the same values to array. What I am doing wrong ?
This:
foreach ($allCat as $d => $mess) {
$mess is a temporary COPY of whatever value your foreach() loop is currently working on. When you do your unset($mess...) later on, you're simply unsetting that temporary copy.
While some might suggest making $mess a reference, this can/will cause problems later on, because $mess will STILL be a reference after the loops end, and reusing the variable later on will now be mucking around with whatever $mess last pointed at in the loop.
Instead, use the full array/object path reference in the unset call:
unset($messages[$domain][$d][$ymlkey][$ymkl])
or whatever it should be. This way you guarantee you're working with the actual "real" array, and not any of the many temporary copies your nested loops are creating.

foreach loop over multidimensional associative array

I have an array ($this->taxBand) which has 3 key => value pairs. Each value is another array:
array(3) {
["basic"]=>
array(3) {
["rate"]=>
int(20)
["start"]=>
int(0)
["end"]=>
int(31865)
}
["higher"]=>
array(3) {
["rate"]=>
int(40)
["start"]=>
int(31866)
["end"]=>
int(150000)
}
["additional"]=>
array(3) {
["rate"]=>
int(45)
["start"]=>
int(150001)
["end"]=>
NULL
}
}
I need to loop through not only the keys "basic", "higher" and "additional" but the arrays within them too.
I'll be comparing the "start" and "end" values with another variable and applying a calculation based on the "rate".
I've tried nested foreach in a couple of different ways using many examples I've found here and the official documentation and can only ever get it to return the "basic" array elements.
Example:
foreach ($this->taxBand as $key => $band) {
foreach ($band as $subKey => $value) {
// Do my stuff
}
}
If I return $band, I get:
array(5) {
["rate"]=>
int(20)
["start"]=>
int(0)
["end"]=>
int(31865)
}
And $key returns:
string(5) "basic"
I'm clearing missing something quite basic and not fully understanding how to loop through these arrays properly and get all the data I need.
Any help would be much appreciated. :)
EDIT: Trying to show an example of how I plan to use this loop. It's difficult because of the other functions/variables:
foreach ($this->taxBand as $key => $band) {
if ($band["end"] !== null || $band["end"] > 0) {
$band["amount"] = $this->get_lower_figure($this->totalTaxableAmount, $band["end"]) - $bandDeductions;
} else {
$band["amount"] = $this->totalTaxableAmount - $bandDeductions;
}
$band["percentage_amount"] = ($band["amount"] / 100) * $band["rate"];
$totalDeduction += $band["percentage_amount"];
$bandDeductions += $band["amount"];
return $totalDeduction;
}
Assuming $this->totalTaxableAmount is 40000 the "percentage_amount" should return a float of 6373, which it does, for the basic band. But it should also return 3254 from the higher band.
get_lower_figure() just takes two arguments and checks which is less than the other.
You seem to be going about everything right..but if you are going to be using the start/end/rate values directly, you shouldn't need the second foreach loop. Something like this should get you going:
$values = array();
foreach($this->taxBand as $key => $band) {
$calculation = ($band['end'] - $band['start']) / $band['rate'];
$values[$key] = $calculation;
}
return $values;
I'm doubting this is the work you plan to do, but the point is you can access the inner associative array $band directly.
If you were to use a sub-loop for whatever reason, you could do something like this:
foreach($this->taxBand as $key => $band) {
// Set variables for this band
foreach($band as $subKey => $value) {
switch($subKey) {
case 'start': // do something
break;
case 'end': // do something
break;
case 'rate': // do something
break;
}
}
// Calculations finished
}

Categories