Loop into multidimensional array from top to bottom - php

I have this tree :
Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
),
[1] => Array
(
[id] => 5
[parent_id] => 1
[title] => Sub Page 2
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)
And I'm looking for a display from top to bottom.
And display something like this :
1
1.2
1.5
4
But if I have id 3 which is a leaf from 5 I would like this :
1
1.2
1.5
1.5.3
4
I have search a lot and my brain is limited when i'm using recursivity..
I have tried this :
function printAll($a){
foreach ($a as $v){
if (!array_key_exists('children', $v)){
debugLog($v['id']);
return;
}
else{
$arrayChildrens = $v['children'];
foreach($arrayChildrens as $c){
$arrayChildrens = $c['children'];
$this->printAll($arrayChildrens);
}
}
}
}
But doesn't work..
I tried to begin just to display
1
2
5
4
But my goal is to display id parents before id ( like Ishowed you before)
Thanks a lot !

This function should give you your expected output.
function printAll($a, $prefix = '') {
//loop through $a
foreach($a as $v) {
//echo current level `id` with previous `$prefix`
echo "{$prefix}{$v['id']}\n";
//check if current level contains children
if(!empty($v['children'])) {
//clean up prefix to remove extra `.` at the end of prefixes
$prev_prefix = rtrim($prefix, '.');
//recurse printAll again passing the children as `$a` and a `$prefix` being the previous levels combined e.g `1.5`
//also clean up extra periods at the start of the prefix
printAll($v['children'], ltrim("{$prev_prefix}.{$v['id']}.", "."));
}
}
}
Output:
1
1.2
1.5
1.5.3
4
Using a proper return
Usually with a function you actually want the function to return values instead of echoing them automatically to your page. If you want this function to return an array of values instead of echoing them, you could do this:
function printAll($a, $level = '', $values = []) {
foreach($a as $v) {
$values[] = $value = "{$level}{$v['id']}";
if(!empty($v['children'])) {
$values = printAll($v['children'], "{$value}.", $values);
}
}
return $values;
}
Which will have a result like this:
Array
(
[0] => 1
[1] => 1.2
[2] => 1.5
[3] => 1.5.3
[4] => 4
)

This should do the job.
$arr = array(
array(
'id' => 1,
'parent_id' => 0,
'title' => 'Parent Page',
'children' => array(
array(
'id' => 2,
'parent_id' => 1,
'title' => 'Sub Page',
),
array(
'id' => 5,
'parent_id' => 1,
'title' => 'Sub Page 2',
'children' => array(
array(
'id' => 7,
'parent_id' => 5,
'title' => 'Sub Page',
),
array(
'id' => 8,
'parent_id' => 5,
'title' => 'Sub Page 2',
)
)
)
)
),
array(
'id' => 4,
'parent_id' => 0,
'title' => 'Another Parent Page',
)
);
function printAll($arr, $parent = [])
{
if (is_array($arr)) {
foreach ($arr as $k => $v) {
if (isset($v['id'])) {
$parent[] = $v['id'];
echo implode('.', $parent) . PHP_EOL;
}
if (isset($v['children'])) {
printAll($v['children'], $parent);
}
array_pop($parent);
}
}
}
printAll($arr);
Output
1
1.2
1.5
1.5.7
1.5.8
4
Working demo.

Related

How to merge two arrays diferents on one

How to update an array of objects, adding the quantities if you already have the same ID, or if you have not created a new object.
I tried to explain in the code with the arrays and also with the idea of how I would like the result to be.
old Array
$a1 = [
array(
"id" => 1,
"qty" => 1
),
array(
"id" => 2,
"qty" => 1
)
];
$a2 = [
array(
"id" => 1,
"qty" => 1
)
];
$output = array_merge($a1, $a2);
echo '<pre>';
print_r($output);
echo '</pre>';
Result Error:
Array
(
[0] => Array
(
[id] => 1
[qty] => 1
)
[1] => Array
(
[id] => 2
[qty] => 1
)
[2] => Array
(
[id] => 1
[qty] => 1
)
)
What I need, in addition to if the ID does not contain, add.
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)
You can take the first array as base, then search for the key (if existing) where the product matches the id. Then either add the quantity and recalculate the price or you just add the reformatted element (id to product conversion).
$result = $a;
foreach($b as $element) {
$matchingProductIndex = array_search($element['id'], array_column($a, 'product'));
if ($matchingProductIndex !== false) {
$pricePerUnit = $result[$matchingProductIndex]['price'] / $result[$matchingProductIndex]['qty'];
$result[$matchingProductIndex]['qty'] += $element['qty'];
$result[$matchingProductIndex]['price'] = $result[$matchingProductIndex]['qty'] * $pricePerUnit;
} else {
$result[] = [
'qty' => $element['qty'],
'product' => $element['id'],
'price' => $element['price'],
];
}
}
print_r($result);
Working example.
Loop through both arrays with foreach and check the ids against each other.
https://paiza.io/projects/lnnl5HeJSFIOz_6KD6HRIw
<?php
$arr1 = [['qty' => 4, 'id' => 4],['qty' => 1,'id' => 30]];
$arr2 = [['id' => 30, 'qty' => 19],['id' => 31, 'qty' => 2]];
$arr3 = [];
foreach($arr1 as $iArr1){
$match = false;
foreach($arr2 as $iArr2){
if($iArr1['id'] === $iArr2['id']){
$arr3[] = ['id' => $iArr1['id'], 'qty' => $iArr1['qty'] + $iArr2['qty']];
$match = true;
}
}
if(!$match){
$arr3[] = $iArr1;
$arr3[] = $iArr2;
}
}
print_r($arr3);
?>
One approach could be one I more often suggested.
First lets merge $a2 with one to simplify looping over one larger collection.
If we then create a small mapping from id to its index in the result array we can update the running total of qty.
$map = [];
$result = [];
// Merge the two and do as per usual, create a mapping
// from id to index and update the qty at the corresponding index.
foreach (array_merge($a1, $a2) as $subarr) {
$id = $subarr['id'];
if (!key_exists($id, $map)) {
$index = array_push($result, $subarr) - 1;
$map[$id] = $index;
continue;
}
$result[$map[$id]]['qty'] += $subarr['qty'];
}
echo '<pre>', print_r($result, true), '</pre>';
Output:
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)

PHP sum by other field from unique id in array

I have an multidimensional array like this one:
Array
(
[0] => array('id'=>1,'name'=>'Agent 1','total'=>3)
[1] => array('id'=>2,'name'=>'Agent 2','total'=>3)
[2] => array('id'=>3,'name'=>'Agent 3','total'=>3)
[3] => array('id'=>1,'name'=>'Agent 1','total'=>6)
)
And I want to remove duplicate agents from this array and sum the total field to end up in a array like this:
Array
(
[0] => array('id'=>1,'name'=>'Agent 1','total'=>9)
[1] => array('id'=>2,'name'=>'Agent 2','total'=>3)
[2] => array('id'=>3,'name'=>'Agent 3','total'=>3)
)
I have tried array_unique but it only remove duplicates...
Try this code: sandbox code
Main idea of algorithm - caching key pairs in result array and further checking existence of them.
$array = [
0 => ['id' => 1, 'name' => 'Agent 1', 'total' => 3],
1 => ['id' => 2, 'name' => 'Agent 2', 'total' => 3],
2 => ['id' => 3, 'name' => 'Agent 3', 'total' => 3],
3 => ['id' => 1, 'name' => 'Agent 1', 'total' => 6],
];
$sumArray = [];
foreach ($array as $agentInfo) {
// create new item in result array if pair 'id'+'name' not exists
if (!isset($sumArray[$agentInfo['id'].$agentInfo['name']])) {
$sumArray[$agentInfo['id'].$agentInfo['name']] = $agentInfo;
} else {
// apply sum to existing element otherwise
$sumArray[$agentInfo['id'].$agentInfo['name']]['total'] += $agentInfo['total'];
}
}
// optional action to flush keys of array
$sumArray = array_values($sumArray);
print_r ($sumArray);
Try this,
$arrays = array_values(array_combine(array_map(function ($i) { return $i['id']; }, $array), $array));
print_r($arrays);
DEMO
assuming an id is unique you could use this
$array = array(
array('id' => 1, 'name' => 'Agent 1', 'total' => 3),
array('id' => 2, 'name' => 'Agent 2', 'total' => 3),
array('id' => 3, 'name' => 'Agent 3', 'total' => 3),
array('id' => 1, 'name' => 'Agent 1', 'total' => 6)
);
$array_ids = array();
foreach ($array as $key => $value) {
if (isset($array_ids[$value['id']])) {
$array[$array_ids[$value['id']]]['total'] += $value['total'];
unset($array[$key]);
}
else
{
$array_ids[$value['id']] = $key;
}
}
in this way you save the used id's into array $array_ids, with that you can easily check if an agent already exists in the array
In order to achieve the exact output you desire, you will need a nested loop.
$input = array(
0 => array('id'=>1,'name'=>'Agent 1','total'=>3),
1 => array('id'=>2,'name'=>'Agent 2','total'=>3),
2 => array('id'=>3,'name'=>'Agent 3','total'=>3),
3 => array('id'=>1,'name'=>'Agent 1','total'=>6)
);
// This is where we will save our result
$output = array();
foreach ($input as $item) {
// Used to determine if the current $item
// already exists in the $output array
$foundItem = false;
// Now we check the $item against every output entry
foreach ($output as &$entry) {
if ($entry["id"] == $item["id"]) {
// Since we found a match, let's add the
//current item's total to the output total.
$entry["total"] += $item["total"];
// Marking this as true will later prevent us
// from inserting the item to the output array twice
$foundItem = true;
}
}
// If the item was not found in the output array
// the $foundItem variable remains false
// Using ! to negate the boolean, we insert the item to the output array
if (!$foundItem) {
$output[] = $item;
}
}
Realize that this is not the only way to get the required output. This only is the simplest solution and can certainly be improved in many ways. However, I will leave that part up to you.
I have tried using array_reduce - Iteratively reduce the array to a single value using a callback function. And written a function according to the requirements. Hope this will help you.
<?php
$array = [
0 => ['id' => 1, 'name' => 'Agent 1', 'total' => 3],
1 => ['id' => 2, 'name' => 'Agent 2', 'total' => 3],
2 => ['id' => 3, 'name' => 'Agent 3', 'total' => 3],
3 => ['id' => 1, 'name' => 'Agent 1', 'total' => 6],
];
$result = array_reduce($array, function($temp, $item){
isset($temp[$item['id']])
? $temp[$item['id']]['total'] += $item['total']
: $temp[$item['id']] = $item;
return $temp;
}, []);
print_r($result);
?>
OUTPUT
Array
(
[1] => Array ( [id] => 1 [name] => Agent 1 [total] => 9 )
[2] => Array ( [id] => 2 [name] => Agent 2 [total] => 3 )
[3] => Array ( [id] => 3 [name] => Agent 3 [total] => 3 )
)

Best way to calculate overall records based on associative data

I'm processing the final results of competitions and its general report on the best trainer and which place the trainer should get.
I have already prepared associative arrays below. The key represents trainer's id and the value represents the number of medals in a category (gold, silver, bronze) that his/her athletes got.
[gold] => Array
(
[777777] => 4
[333333] => 2
[555555] => 1
[999999] => 1
)
[silver] => Array
(
[999999] => 3
[777777] => 3
[333333] => 2
)
[bronze] => Array
(
[333333] => 6
[777777] => 4
[999999] => 2
)
Next array associates trainer's id with its name:
[trainers] => Array
(
[333333] => Trainer 4
[777777] => Trainer 1
[999999] => Trainer 2
[555555] => Trainer 3
)
I have stuck processing the data above into final results like this. Any ideas on how it could be done elegantly? The problem is that the data is never constant and the size of the array is always different.
Any help would be greatly appreciated.
Here is code sample:
$gold, $silver, $bronze, $trainers are arrays with information you provided.
$out = [];
foreach($trainers as $trainerId=> $trainerName){
$out[] = array(
'id'=>$trainerId,
'name'=>$trainerName,
'gold'=>isset($gold[$trainerId])?$gold[$trainerId]:0,
'silver'=>isset($silver[$trainerId])?$silver[$trainerId]:0,
'bronze'=>isset($bronze[$trainerId])?$bronze[$trainerId]:0,
);
}
uasort($out, function($a, $b){
// Here: sort by your algorithm. Here is example:
if($a['gold'] != $b['gold']){
return $b['gold'] - $a['gold'];
}
if($a['silver'] != $b['silver']){
return $b['silver'] - $a['silver'];
}
return $b['bronze'] - $a['bronze'];
});
$placeId = 1;
foreach($out as &$info){
$info['place'] = $placeId++;
}
unset($info);
foreach($out as $info){
echo "{$info['place']} place goes to - {$info['name']} ({$info['id']}) as he/she got {$info['gold']} gold medals, {$info['silver']} silver and {$info['bronze']} bronze";
}
Here is another way to do it with metrics:
$gold = array
(
'777777' => 4,
'333333' => 2,
'555555' => 1,
'999999' => 1
);
$silver = array
(
'999999' => 3,
'777777' => 3,
'333333' => 2
);
$bronze = array
(
'333333' => 6,
'777777' => 4,
'999999' => 2
);
$trainers = array
(
'333333' => 'Trainer 4',
'777777' => 'Trainer 1',
'999999' => 'Trainer 2',
'555555' => 'Trainer 3'
);
$metrics = [
'gold' => 3,
'silver'=> 2,
'bronze' => 1];
$results = [];
foreach ($metrics as $arrName => $metric)
{
foreach (${$arrName} as $trainerId => $medals)
{
$results[$trainerId] = ( isset($results[$trainerId]) ) ? $results[$trainerId]+$medals * $metric : $medals * $metric;
}
}
// sorting scores (by value)
arsort($results);
// print scores
var_dump($results);
// print final results
$placeOut = '';
foreach ($results as $trainerId => $score) {
$placeOut .= $trainers[$trainerId].": he/she has ";
foreach ($metrics as $medalName => $metric) {
$placeOut .= (${$medalName}[$trainerId] > 0 ? ${$medalName}[$trainerId] : 0)." ".$medalName.", ";
}
$placeOut .= "\n";
}
echo "<pre>".$placeOut."</pre>";
?>

How to check if the same value exists in two arrays with php [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How check any value of array exist in another array php?
I am creating a shopping site. To simplify, I have 2 arrays, one holds all my items and the other holds all the items that are added on the cart :
$Items
Array
(
[0] => stdClass Object
(
[id] => 1
[title] => Verity soap caddy
[price] => 6.00
)
[1] => stdClass Object
(
[id] => 2
[title] => Kier 30cm towel rail
[price] => 14.00
)
//a lot more
)
$cartItems
Array
(
[0] => Array
(
[rowid] => c4ca4238a0b923820dcc509a6f75849b
[id] => 1
[qty] => 1
[price] => 6.00
[name] => Verity soap caddy
[subtotal] => 6
)
)
I would like to loop through the $cartItems and add a class (identify) if an item is also in the cart. This is how I tried to do it
foreach($items as $items){
if($cartItems[$items->id]['id']){
echo '<h1 class="inCart">'. $item->title . '</h1>' //...
}else{
echo '<h1>'. $item->title . '</h1>' //...
}
}
The above code does not work - even though $cartItems[0]['id'] would return what I need. My thinking is, whilst looping through $items, check if the similar id exists in the $cartItems array. I also tried to add $cartItems[$i]['id'] and increment $i within the loop, and that did not work.
Of course the html output that I wanted to get is (simplified)
<h1 class="IsOnCart"> Item Title</h1>
<h1> Item Title</h1>
<h1 class="IsOnCart"> Item Title</h1>
<h1> Item Title</h1>
Is there a way to implement this?
Thanks
$intersection = array_intersect($arr1, $arr2);
if (in_array($value, $intersection)) {
// both arrays contain $value
}
You can try
$items = array(
0 =>
(object) (array(
'id' => 1,
'title' => 'Verity soap caddy',
'price' => '6.00',
)),
1 =>
(object) (array(
'id' => 2,
'title' => 'Kier 30cm towel rail',
'price' => '14.00',
)),
);
$cartItems = array(
0 => array(
'rowid' => 'c4ca4238a0b923820dcc509a6f75849b',
'id' => 1,
'qty' => 1,
'price' => '6.00',
'name' => 'Verity soap caddy',
'subtotal' => 6,
),
);
$itemsIncart = array_reduce(array_uintersect($items, $cartItems, function ($a, $b) {
$a = (array) $a;
$b = (array) $b;
return $a['id'] === $b['id'] ? 0 : 1;
}), function ($a, $b) {
$a[$b->id] = true;
return $a;
});
foreach ( $items as $item ) {
if (array_key_exists($item->id, $itemsIncart))
printf('<h1 class="inCart">%s *</h1>', $item->title);
else
printf('<h1>%s</h1>', $item->title);
}
Output
<h1 class="inCart">Verity soap caddy</h1>
<h1>Kier 30cm towel rail</h1>

multidimensional array from nested array

I have a script that goes through a CSV file and puts each row as an array into another array (nested array?). Each row has 2-3 fields for the category of the item in that row. I'm trying to work through how to create a multidimensional array out of these categories. Here is the source I currently have:
$csv = new File_CSV_DataSource;
if ($csv->load($file)) {
$items = $csv->getHeaders();
$csv->getColumn($items[2]);
if ($csv->isSymmetric()) {
$items = $csv->connect();
} else {
$items = $csv->getAsymmetricRows();
}
$items = $csv->getrawArray();
}
$mainCats = array();
$subCats = array();
$subSubs = array();
foreach($items as $item){
if(!in_array($item[10], $mainCats)){
$mainCats[] = $item[10];
}
}
foreach($items as $item){
if(!array_key_exists($item[11], $subCats)){
$parent = array_search($item[10], $mainCats);
$subCats[$item[11]] = $parent;
}
}
foreach($items as $item){
if(!array_key_exists($item[12], $subSubs)){
$parent = array_search($item[11], array_keys($subCats));
$subSubs[$item[12]] = $parent;
}
}
What this does so far is create 3 arrays with the format of:
$mainCats = Array(
[0] => Main Cat 1,
[1] => Main Cat 2,
[2] => Main Cat 3
);
$subCats = Array(
[Sub Cat 1] => 0,
[Sub Cat 2] => 1,
[Sub Cat 3] => 2
);
$subSubs = Array(
[Sub Sub 1] => 0,
[Sub Sub 2] => 1,
[Sub Sub 3] => 2
);
The numeric values of each of the last 2 arrays are the index of their parent category in the previous array. What I would like to do is to merge them all into one large array in the format of:
$cats = Array(
[0] => Array(
'name' => Main Cat 1,
'subs' => Array(
[0] => Array(
'name' => Sub Cat 1,
'subs' => Array(
'name' => Sub Sub 1
)
)
)
),
[1] => Array(
'name' => Main Cat 2,
'subs' => Array(
[0] => Array(
'name' => Sub Cat 2,
'subs' => Array(
'name' => Sub Sub 2
)
)
)
),
[2] => Array(
'name' => Main Cat 3,
'subs' => Array(
[0] => Array(
'name' => Sub Cat 3,
'subs' => Array(
'name' => Sub Sub 3
)
)
)
),
);
I know there has to be a far more efficient way of doing this, but I can't figure it out.
EDIT - I should also mention that not all rows have a 3rd category field value.
I prefer to index them by name:
$cats=array();
//--------
foreach($items as $item){
$main=$item[10];
$subCat=$item[11];
$subSub[$item[12]];
$cats[$main]['subs'][$subCat]['subsubs'][$subSub]['name']=$subSub;
$cats[$main]['subs'][$subCat]['name']=$subCat;
$cats[$main]['name']=$main;
}

Categories