I have this array:
$data = [
["id" => 1, "data" => "data 1"],
["id" => 2, "data" => "data <4>"],
["id" => 3, "data" => "data 3"],
["id" => 4, "data" => "<3>"]
];
I want to produce a new array. The resulting array should be:
[
["id" => 1, "data" => "data 1"],
["id" => 2, "data" => "data data 3"],
["id" => 3, "data" => "data 3"],
["id" => 4, "data" => "data 3"]
]
The idea is that each time there is <number>
in the data
attribute then this value should be replaced by the data
attribute in the element which has the same id
. In the example above, the last element is: ["id" => 4, "data" => "<3>"]
So we replace <3>
with data 3
since it is what is stored in the data
attribute of element with id => 3
.
I have already created a function that works with the above array:
public function refProcess($data, &$newData, $i, &$tmpData){
$dataLength = count($data);
if($i>=$dataLength){
return;
}
for(;$i<$dataLength;$i++){
if(is_null($tmpData)){
$tmpData = ['id'=> $data[$i]['id'], 'data'=>null];
}
if(strpos($data[$i]['data'],"[")!==false){
$parsed = $this->getInbetweenStrings("<", ">", $data[$i]['data']);
if(count($parsed)){
foreach($parsed as $occurance){
foreach($data as $key => $dataValue){
if($dataValue['id']==$occurance){
if(strpos($dataValue['data'], "<")!==false){
$this->refProcess($data, $newData, $key, $tmpData);
$tmpData=null;
}
else{
$tmpDataAtt = str_replace("<".$occurance.">", $dataValue['data'], $data[$i]['data']);
$tmpData['data'] = $tmpDataAtt;
$newData [] = $tmpData;
$tmpData = null;
break;
}
}
}
}
}
}
else{
$tmpData['data'] = $data[$i]['data'];
$newData [] = $tmpData;
$tmpData = null;
}
}//foreach
}
//returns an array contains strings existing between $start and $end. Multiple occurance public function getInbetweenStrings($start, $end, $str){ $matches = array(); $regex = "/$start([a-zA-Z0-9_]*)$end/"; preg_match_all($regex, $str, $matches); return $matches[1]; }
It works fine until I add another element to the array:
$data = [
["id" => 1, "data" => "data 1"],
["id" => 2, "data" => "data <4>"],
["id" => 3, "data" => "data 3"],
["id" => 4, "data" => "<3>"]
["id" => 5, "data" => "<2>"]
];
Element with id:5
the function goes into an endless loop. What am I missing?
The code can be tested at https://onlinephp.io/c/0da5d
Your code is too clumsy to begin with. I would rather suggest a much simpler approach.
Index your array with id
value as it's key with array_column
. This way, we can access any index with id
of a particular value in O(1) time. This also gives you an advantage for the value of the id
to be anything and not necessarily being symmetric with your subarray index key.
Capture the ID using regex. If there is a match, recurse again, else, our search ends here. Return it's data
value to the parent call and you are done.
Snippet:
<?php
$data = array_column($data, null, 'id');
foreach($data as $id => $d){
getLeafNodeValue($id, $data);
}
function getLeafNodeValue($id, &$data){
$matches = [];
if(preg_match('/<(\d+)>/', $data[$id]['data'], $matches) === 1){
$data[$id]['data'] = preg_replace('/<(\d+)>/', getLeafNodeValue($matches[1], $data), $data[$id]['data']);
}
return $data[$id]['data'];
}
print_r($data);
Online Demo
You dont really do any recursive stuff, is a quit flat array structure you have.
UPDATE: This was wrong, keep it just for the related comments:
<?php
$data = [
["id" => 1, "data" => "data 1",],
["id" => 2, "data" => "data <4>",],
["id" => 3, "data" => "data 3",],
["id" => 4, "data" => "<3>",],
["id" => 5, "data" => "<2>",]
];
foreach ($data as &$set) {
$set['data'] = preg_replace_callback('#.*(\d+).*#', function ($m) {
return 'data ' . $m[1];
},$set['data']);
}
print_r($data);
UPDATE: Here is now a working solution.
<?php
$data = [
["id" => 1, "data" => "data 1",],
["id" => 2, "data" => "data <4>",],
["id" => 3, "data" => "data 3",],
["id" => 4, "data" => "<3>",],
["id" => 5, "data" => "<2>",],
["id" => 6, "data" => "data <7>",],#loop to 7
["id" => 7, "data" => "<6>",],#loop to 6
["id" => 8, "data" => "<0>",],#dead link
["id" => 9, "data" => "<10>",],#multi level loop
["id" => 10, "data" => "data <11>",],#multi level loop
["id" => 11, "data" => "<9>",],#multi level loop
];
#just for testing: order can be ignored
shuffle($data);
#step 1 make keys easy to access
$prepared = [];
$tmp=[];
foreach ($data as $set) {
$prepared[$set['id']] = $set['data'];
$tmp[$set['id']] = $set;
}
$data=$tmp;
#setp 2 replace values
$final = [];
do {
foreach ($data as $k => &$set) {
$set['data'] = preg_replace_callback('#(.*)<(\d+)>#', function ($m) use ($prepared,$k) {
#check for dead links
if(!isset($prepared[$m[2]])){
return $m[1]." ?{$m[2]}?";
}
#check for loop refer
if(strpos($prepared[$m[2]],"<".$k.">")!==false){
return $m[1]." §{$m[2]}§";
}
return $m[1].$prepared[$m[2]];
}, $set['data']);
if (strpos($set['data'], '>') === false) {
$final[$k] = $set;
unset($data[$k]);
}
}
} while (count($data));
ksort($final);
print_r($final);
UPDATED:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With