Building multidimensional arrays in PHP with foreach - php

How would I go about building output like this in PHP:
$items[] = array(
'section_name' => 'Section 1 Name',
'items' => array(
array(
'item_name' => 'Item 1 Name - Section 1'
// there will be more in here
),
array(
'item_name' => 'Item 2 Name - Section 1'
// there will be more in here
)
)
);
$items[] = array(
'section_name' => 'Section 2 Name',
'items' => array(
array(
'item_name' => 'Item 1 Name - Section 2'
// there will be more in here
),
array(
'item_name' => 'Item 2 Name - Section 2'
// there will be more in here
)
)
);
// and so on
with this as the input
[section] => Array (
[0] => Array (
[name] => Section 1 Name
[item] => Array (
[0] => Array (
[name] => Item 1 Name - Section 1
// there will be more in here
)
[1] => Array (
[name] => Item 2 Name - Section 1
// there will be more in here
)
)
)
[1] => Array (
[name] => Section 2 Name
[item] => Array (
[0] => Array (
[name] => Item 1 Name - Section 2
// there will be more in here
)
)
)
)
There will never be a set number of items in a section and the number of sections will vary too so I need something iterative then a fixed number.

Something like this ? :
$sections = [];
for ($sectionIndex = 0; $sectionIndex < 10; ++$sectionIndex) {
$items = [];
for ($itemIndex = 0; $itemIndex < 10; ++$itemIndex) {
$items[] = [
'item_name' => sprintf('Item %d Name - Section %d', $itemIndex, $sectionIndex);
];
}
$sections[] = [
'section_name' => sprintf("Section %d Name", $sectionIndex),
'items' => $items
];
}
Replace [] by array, since I don't know which PHP version you're using.

Pass this your input array $section
$items=array();
$item=array();
foreach ($section as $row) {
$tmp=array();
$tmp['section_name']=$row['name'];
foreach($row['item'] as $key){
$item[]['item_name']=$key['name'];
}
$tmp['items']=$item;
$items[]=$tmp;
}
print_r($items);

Solved with a slightly modified version of the answer from #NoDataFound
$sections = [];
$s = 0;
foreach ($_POST['section'] as $section) {
$s++;
$items = [];
$i = 0;
foreach ($section['item'] as $item) {
$i++;
$items[] = [
'item_name' => sprintf('Item %d Name - Section %d', $i, $s)
];
}
$sections[] = [
'section_name' => sprintf("Section %d Name", $s),
'items' => $items
];
}

Related

Loop into multidimensional array from top to bottom

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.

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>";
?>

PHP Array duplicate data count

I have title and language data in PHP Array. I need to display repeated title count. Please check below array format.
Array (
[0] => Array ( [title] => My_title1 [language] => English )
[1] => Array ( [title] => My_title1 [language] => English )
[2] => Array ( [title] => My_title2 [language] => Japanese )
)
I need to display data in following format.
Title Language Count
My_title1 English 2
My_title2 Japanese 1
I tried following code
$count = array_count_values(array_map(
function($item) {
return $item['title'];
}, $testArray));
print_r($count);
but I got only title counts.
Array ( [My_title1] => 2 [My_title2] => 1 )
How do I display like following format?
Title Language Count
My_title1 English 2
My_title2 Japanese 1
You can do it for example this way:
<?php
$testArray = array (
0 => Array ( 'title' => 'My_title1', 'language' => 'English' ),
1 => Array ( 'title' => 'My_title1', 'language' => 'English' ),
2 => Array ( 'title' => 'My_title2', 'language' => 'Japanese' ),
);
$count = array_count_values(array_map(
function($item) {
return $item['title'].'#'.$item['language'];
}, $testArray));
$outArray = array();
foreach ($count as $k => $v) {
$k = explode('#', $k);
$record['Title'] = $k[0];
$record['Language'] = $k[1];
$record['Count'] = $v;
$outArray[] = $record;
}
var_dump($outArray);
Of course you need to use character that will not be used inside title and language, probably # is quite good enough
Try this code code which i created just for this assignment
<?php
$array = array(
0 => array ( 'title' => 'My_title1', 'language' => 'English'),
1 => array ( 'title' => 'My_title1', 'language' => 'English'),
2 => array ( 'title' => 'My_title2', 'language' => 'Japanese')
);
$final = array();
foreach($array as $key =>$value)
{
$flag = 0;
foreach($final as $key1 =>$value1)
{
if($array[$key]['title'] == $final[$key1]['title'])
{
$final[$key1]['count']++;
$flag = 1;
}
}
if($flag == 0)
{
$push = array('title' => $array[$key]['title'],'language' => $array[$key]['language'],'count' =>1);
array_push($final,$push);
}
}
echo "<pre>";print_r($final);

PHP Counting inside an Array

I want to create a list where if its already in the array to add to the value +1.
Current Output
[1] => Array
(
[source] => 397
[value] => 1
)
[2] => Array
(
[source] => 397
[value] => 1
)
[3] => Array
(
[source] => 1314
[value] => 1
)
What I want to Achieve
[1] => Array
(
[source] => 397
[value] => 2
)
[2] => Array
(
[source] => 1314
[value] => 1
)
My current dulled down PHP
foreach ($submissions as $timefix) {
//Start countng
$data = array(
'source' => $timefix['parent']['id'],
'value' => '1'
);
$dataJson[] = $data;
}
print_r($dataJson);
Simply use an associated array:
$dataJson = array();
foreach ($submissions as $timefix) {
$id = $timefix['parent']['id'];
if (!isset($dataJson[$id])) {
$dataJson[$id] = array('source' => $id, 'value' => 1);
} else {
$dataJson[$id]['value']++;
}
}
$dataJson = array_values($dataJson); // reset the keys - you don't nessesarily need this
This is not exactly your desired output, as the array keys are not preserved, but if it suits you, you could use the item ID as the array key. This would simplify your code to the point of not needing to loop through the already available results:
foreach ($submissions as $timefix) {
$id = $timefix['parent']['id'];
if (array_key_exists($id, $dataJson)) {
$dataJson[$id]["value"]++;
} else {
$dataJson[$id] = [
"source" => $id,
"value" => 1
];
}
}
print_r($dataJson);
You should simplify this for yourself. Something like:
<?
$res = Array();
foreach ($original as $item) {
if (!isset($res[$item['source']])) $res[$item['source']] = $item['value'];
else $res[$item['source']] += $item['value'];
}
?>
After this, you will have array $res which will be something like:
Array(
[397] => 2,
[1314] => 1
)
Then, if you really need the format specified, you can use something like:
<?
$final = Array();
foreach ($res as $source=>$value) $final[] = Array(
'source' => $source,
'value' => $value
);
?>
This code will do the counting and produce a $new array as described in your example.
$data = array(
array('source' => 397, 'value' => 1),
array('source' => 397, 'value' => 1),
array('source' => 1314, 'value' => 1),
);
$new = array();
foreach ($data as $item)
{
$source = $item['source'];
if (isset($new[$source]))
$new[$source]['value'] += $item['value'];
else
$new[$source] = $item;
}
$new = array_values($new);
PHP has a function called array_count_values for that. May be you can use it
Example:
<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>
Output:
Array
(
[1] => 2
[hello] => 2
[world] => 1
)

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