Php parse multilevel - xml into array - php

I am trying to convert xml into php array but somehow i am doing a mistake, can anyone please help me ?
Here the xml formate :
<Department Id="3">
<Week Date="23/03/2020">
<Class DateTime="23/03/2020 18:00"/>
<Class DateTime="23/03/2020 18:45"/>
</Week>
<Week Date="30/03/2020">
<Class DateTime="30/03/2020 18:00"/>
<Class DateTime="30/03/2020 18:45"/>
</Week>
</Department>
Output Need like this :
Array
(
[0] => Array
(
[0] => Array
(
[DateTime] => 23/03/2020 18:00
)
[1] => Array
(
[DateTime] => 23/03/2020 18:45
)
)
[1] => Array
(
[0] => Array
(
[DateTime] => 30/03/2020 18:00
)
[1] => Array
(
[DateTime] => 30/03/2020 18:45
)
)
)
This is what i have tried
foreach ($xml->children() as $week) {
foreach ($week->children() as $class) {
$j = 0;
foreach ($class->attributes() as $a => $b){
$results[$i][$j][$a] = (string) $b;
}
$j++;
}
$i++;
}
I dont know whats wrong in my code :(

It's just a case of getting the right levels in the XML to match the loops, this builds a weeks data at a time and adds it into the overall results...
$results = [];
foreach ( $xml->Week as $week ) {
$weekData = [];
foreach ( $week->Class as $class ) {
$weekData[]['DateTime'] = (string)$class['DateTime'];
}
$results[] = $weekData;
}
To make this load all attributes...
$results = [];
foreach ( $xml->Week as $week ) {
$weekData = [];
foreach ( $week->Class as $class ) {
$classData = [];
foreach ( $class->attributes() as $name => $value ) {
$classData[$name] = (string)$value;
}
$weekData[] = $classData;
}
$results[] = $weekData;
}

Related

How to parse arrays with different levels PHP

In a foreach loop i would like to compare [name] value beetween different arrays but they have not the same levels.
Array(
[array1] => Array
(
[0] => WP_Term Object
(
[name] => Plafond
)
)
[array2] => WP_Term Object
(
[name] => Chaudière
)
[array3] => Array
(
[0] => WP_Term Object
(
[name] => Pla
)
[1] => WP_Term Object
(
[name] => Toc
)
)
)
I don't know how could i get the [name] in the same loop whereas levels are different.
I have tried to make :
foreach( $fields as $name => $value )
{
echo $value->name; }
Should i add another loop in the first loop ?
thanks
So your data looks like this:
$json = '{"array1":[{"name":"Plafond"}],"array2":{"name":"Chaudière"},"array3":[{"name":"Pla"},{"name":"Toc"}]}';
$array = json_decode($json);
If you don't know how deep it will go, a simple recursive function should work. Perhaps something like this:
function get_name($o, &$output) {
if (is_array($o)) {
foreach($o as $v) {
get_name($v, $output);
}
} elseif (property_exists($o, "name")) {
$output[] = $o->name;
}
}
$output = [];
foreach ($array as $v) {
get_name($v, $output);
}
If you data is going to look like the sample you provided (i.e. it will always be first or second level) then you don't need to worry about recursion.
$output = [];
foreach ($array as $k=>$v) {
if (is_array($v)) {
foreach ($v as $k2=>$v2) {
$output[] = $v2->name;
}
} else {
$output[] = $v->name;
}
}
Either way, your output values are all in the $output array:
print_r($output);
Output:
Array
(
[0] => Plafond
[1] => Chaudière
[2] => Pla
[3] => Toc
)
You can use array_map, array_key_exists to retrive the name index from the array
$jsonFormat = '{"array1":[{"name":"Plafond"}],"array2":{"name":"Chaudière"},"array3":[{"name":"Pla"},{"name":"Toc"}]}';
$jsonArray = json_decode($jsonFormat,true);
$res = [];
array_map(function($v) use (&$res){
if(array_key_exists('name', $v)){
$res[] = $v['name'];
}else{
foreach($v as $_key => $_value){
$res[] = $_value['name'];
}
}
}, $jsonArray);
echo '<pre>';
print_r($res);
Result:-
Array
(
[0] => Plafond
[1] => Chaudière
[2] => Pla
[3] => Toc
)
You can use $res to compare the names.

How to make an array from foreach?

How to make this array from foreach in my code?
Below is my code
Array
(
[0] => Array
(
[day] => 2016-11-21
)
[1] => Array
(
[day] => 2016-11-22
)
[2] => Array
(
[day] => 2016-11-23
)
[3] => Array
(
[day] => 2016-11-24
)
[4] => Array
(
[day] => 2016-11-25
)
)
Below is my code but the result is different.
The result is from foreach and I want it to be an array, just like the array above.
foreach ($data['my_undertime'] as $undertime_row) {
$datetimein = new DateTime($undertime_row->timein);
$datetimein->format('h:m:s');
$datetimeout = new DateTime($undertime_row->timeout);
$datetimeout->format('h:m:s');
$items = array();
if ($datetimein->format('h:m:s') > $undertime_row->beg_time && $datetimeout->format('h:m:s') < $undertime_row->end_time) {
// echo $undertime_row->day;
$items[]['day'] = $undertime_row->day;
}
echo "<pre>";
print_r($items);
}
Please help.
Thanks.
The problem is in the line $items = array();.
As it is now, $item is recreated on each loop. After the foreach it ends up remembering only the last iteration of the loop.
If you want to keep all the data in $item then move this line of code before the foreach loop.
$items = array();
$i = 0;
foreach ($data['my_undertime'] as $undertime_row) {
$datetimein = new DateTime($undertime_row->timein);
$datetimein->format('h:m:s');
$datetimeout = new DateTime($undertime_row->timeout);
$datetimeout->format('h:m:s');
if ($datetimein->format('h:m:s') > $undertime_row->beg_time && $datetimeout->format('h:m:s') < $undertime_row->end_time) {
// echo $undertime_row->day;
$items[$i]['day'] = $undertime_row->day;
}
$i++;
}
echo "<pre>";
print_r($items);
try this

PHP array and foreach

This is my current code:
$exceptions = array();
foreach ($rows as $row) {
$opens = $row['opens'];
$closes = $row['closes'];
$joined = array($opens, $closes);
$exception = join('-', $joined);
$exceptions[] = array (
$row['date'] => array($exception),
);
}
Which gives:
Array ( [0] => Array ( [06/09] => Array ( [0] => 01:00-22:00 ) ) [1] => Array ( [06/10] => Array ( [0] => 01:00-22:00 ) ) )
But I'm aiming for this because the plugin requires this form:
Array ( [06/09] => Array ( [0] => 01:00-22:00 ) [06/10] => Array ( [0] => 01:00-22:00 ) )
Is there a way to rearrange the array to achieve this?
// Assumptions
// 1. You have `$first_exception` within scope
// 2. You have `$rows` within scope
$exceptions = array();
foreach ($rows as $row) {
// Assumption: `$row` has key `date`
$exceptions[$row['date']] = array (
$first_exception
);
}
Try this:
$exceptions = array();
foreach ($rows as $row) {
$exceptions[$row['date']] = array ($first_exception);
}
You could try this code. And try to read about arrays http://php.net/manual/en/book.array.php
$exceptions = array();
foreach ($rows as $row) {
$exceptions[$row['date']][] = array($first_exception);
}

Associative index array to associative associative array

Problem
I have an array which is returned from PHPExcel via the following
<?php
require_once 'PHPExcel/Classes/PHPExcel/IOFactory.php';
$excelFile = "excel/1240.xlsx";
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objPHPExcel = $objReader->load($excelFile);
foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) {
$arrayData[$worksheet->getTitle()] = $worksheet->toArray();
}
print_r($arrayData);
?>
This returns:
Array
(
[Films] => Array
(
[0] => Array
(
[0] => Name
[1] => Rating
)
[1] => Array
(
[0] => Shawshank Redemption
[1] => 39
)
[2] => Array
(
[0] => A Clockwork Orange
[1] => 39
)
)
[Games] => Array
(
[0] => Array
(
[0] => Name
[1] => Rating
)
[1] => Array
(
[0] => F.E.A.R
[1] => 4
)
[2] => Array
(
[0] => World of Warcraft
[1] => 6
)
)
)
What I would like to have is
Array
(
[Films] => Array
(
[0] => Array
(
[Name] => Shawshank Redemption
[Rating] => 39
)
[1] => Array
(
[Name] => A Clockwork Orange
[Rating] => 39
)
)
[Games] => Array
(
[0] => Array
(
[Name] => F.E.A.R
[Rating] => 4
)
[1] => Array
(
[Name] => World of Warcraft
[Rating] => 6
)
)
)
The arrays names (Films, Games) are taken from the sheet name so the amount can be variable. The first sub-array will always contain the key names e.g. Films[0] and Games[0] and the amount of these can be varible. I (think I) know I will need to do something like below but I'm at a loss.
foreach ($arrayData as $value) {
foreach ($value as $rowKey => $rowValue) {
for ($i=0; $i <count($value) ; $i++) {
# code to add NAME[n] as keys
}
}
}
I have searched extensively here and else where if it is a duplicate I will remove it.
Thanks for any input
Try
$result= array();
foreach($arr as $key=>$value){
$keys = array_slice($value,0,1);
$values = array_slice($value,1);
foreach($values as $val){
$result[$key][] = array_combine($keys[0],$val);
}
}
See demo here
You may use nested array_map calls. Somehow like this:
$result = array_map(
function ($subarr) {
$names = array_shift($subarr);
return array_map(
function ($el) use ($names) {
return array_combine($names, $el);
},
$subarr
);
},
$array
);
Demo
Something like this should work:
$newArray = array();
foreach ($arrayData as $section => $list) {
$newArray[$section] = array();
$count = count($list);
for ($x = 1; $x < $count; $x++) {
$newArray[$section][] = array_combine($list[0], $list[$x]);
}
}
unset($arrayData, $section, $x);
Demo: http://ideone.com/ZmnFMM
Probably a little late answer, but it looks more like your tried solution
//Films,Games // Row Data
foreach ($arrayData as $type => $value)
{
$key1 = $value[0][0]; // Get the Name Key
$key2 = $value[0][1]; // Get the Rating Key
$count = count($value) - 1;
for ($i = 0; $i < $count; $i++)
{
/* Get the values from the i+1 row and put it in the ith row, with a set key */
$arrayData[$type][$i] = array(
$key1 => $value[$i + 1][0],
$key2 => $value[$i + 1][1],
);
}
unset($arrayData[$type][$count]); // Unset the last row since this will be repeated data
}
I think this will do:
foreach($arrayData as $key => $array){
for($i=0; $i<count($array[0]); $i++){
$indexes[$i]=$array[0][$i];
}
for($i=1; $i<count($array); $i++){
for($j=0; $j<count($array[$i]); $j++){
$temp_array[$indexes[$j]]=$array[$i][$j];
}
$new_array[$key][]=$temp_array;
}
}
print_r($new_array);
EDIT: tested and updated the code, works...

XML into Associative Array using PHP

Can anyone help with converting data from an XML document into an associative array? I'm running into issues given that the XML structure is sort of 3D and the array is more of a 2D structure (please forgive my lack of correct terminology throughout).
The XML elements have attributes, children and grand-children (but I never know their names), so I figured I'd try to make the key in the array a concatenation of each child/attribute name and the value equal to, well, the value. Trouble is I need the attribute name and value as part of the concatenated array key to make it unique...
For example:
<Computer id="1">
<OS>
<Name>Linux</Name>
<Age>Older than me</Age>
</OS>
</Computer>
<Computer id="2">
<OS>
<Name>Windows</Name>
<Age>Not so much</Age>
</OS>
</Computer>
Should ideally give:
[Computer-id-1-OS-Name] = 'Linux'
[Computer-id-1-OS-Age] = 'Older than me'
[Computer-id-2-OS-Name] = 'Windows'
[Computer-id-2-OS-Age] = 'Not so much'
But I'm getting this result:
[Computer-id] = '1'
[Computer-OS-Name] = 'Linux'
[Computer-OS-Age] = 'Older than me'
[Computer-id] = '2'
[Computer-OS-Name] = 'Windows'
[Computer-OS-Age] = 'Not so much'
So that the [Computer-id] key is not unique. I'm using a recursive function to read in the values, but I can't figure how to get the attribute name and attribute value into the name of the subordinate keys...(By the way there is a good reason for doing this seemingly illogical task!)
Any help would be greatly appreciated...
Here is the function which 'flattens' the XML data after it has been read into a multi-dimensional array. I'm not sure I'm going about this the right way!
function flattenArray ($array, $baseName = NULL)
{
reset($array);
while (list ($key, $value) = each($array)) {
$outKey = $key . "-";
if (is_array($value)) {
flattenArray($value, $baseName . $outKey);
} else {
$finalKey = $baseName . rtrim($outKey, '-');
$finalValue = $value;
echo "$finalKey = $finalValue\n";
}
}
}
This worked great for me, and it was simple.
$ob = simplexml_load_file('test.xml');
$json = json_encode($ob);
$array = json_decode($json, true);
One example could be:
$dom = new DOMDocument;
$dom->loadXML(
'<root>
<Computer id="1">
<OS>
<Name>Linux</Name>
<Age>Older than me</Age>
</OS>
</Computer>
<Computer id="2">
<OS>
<Name>Windows</Name>
<Age>Not so much</Age>
</OS>
</Computer>
</root>'
);
$xpath = new DOMXPath($dom);
$result = array();
foreach ($xpath->query('//*[count(*) = 0]') as $node) {
$path = array();
$val = $node->nodeValue;
do {
if ($node->hasAttributes()) {
foreach ($node->attributes as $attribute) {
$path[] = sprintf('%s[%s]', $attribute->nodeName, $attribute->nodeValue);
}
}
$path[] = $node->nodeName;
}
while ($node = $node->parentNode);
$result[implode('/', array_reverse($path))] = $val;
}
print_r($result);
Output:
Array
(
[#document/root/Computer/id[1]/OS/Name] => Linux
[#document/root/Computer/id[1]/OS/Age] => Older than me
[#document/root/Computer/id[2]/OS/Name] => Windows
[#document/root/Computer/id[2]/OS/Age] => Not so much
)
Thats not exactly what you're looking for, but it's a start and can easily be tweaked to give different results.
here's my function to generate associated array, derived from
Recursive cast from SimpleXMLObject to Array
function xml2assoc($obj, &$arr) {
$children = $obj->children();
foreach ( $children as $elementName => $node ) {
if (!isset($arr[$elementName])) {
$arr[$elementName] = array();
}
$temp = array();
$attributes = $node->attributes();
foreach ( $attributes as $attributeName => $attributeValue ) {
$attribName = strtolower(trim((string) $attributeName));
$attribVal = trim((string) $attributeValue);
$temp[$attribName] = $attribVal;
}
$text = (string) $node;
$text = trim($text);
if (strlen($text) > 0) {
$temp ['text='] = $text;
}
$arr[$elementName][] = $temp;
$nextIdx = count($arr[$elementName]);
xml2assoc($node, $arr[$elementName][$nextIdx - 1]);
}
return;
}
$xml = '<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>2</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title>
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
<item>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml> ';
$dom = new SimpleXMLElement($xml);
$arr = array();
xml2assoc($dom, $arr);
print_r($arr);
generated array:
Array
(
[ToUserName] => Array
(
[0] => Array
(
[text=] => toUser
)
)
[FromUserName] => Array
(
[0] => Array
(
[text=] => fromUser
)
)
[CreateTime] => Array
(
[0] => Array
(
[text=] => 12345678
)
)
[MsgType] => Array
(
[0] => Array
(
[text=] => news
)
)
[ArticleCount] => Array
(
[0] => Array
(
[text=] => 2
)
)
[Articles] => Array
(
[0] => Array
(
[item] => Array
(
[0] => Array
(
[Title] => Array
(
[0] => Array
(
[text=] => title1
)
)
[Description] => Array
(
[0] => Array
(
[text=] => description1
)
)
[PicUrl] => Array
(
[0] => Array
(
[text=] => picurl
)
)
[Url] => Array
(
[0] => Array
(
[text=] => url
)
)
)
[1] => Array
(
[Title] => Array
(
[0] => Array
(
[text=] => title
)
)
[Description] => Array
(
[0] => Array
(
[text=] => description
)
)
[PicUrl] => Array
(
[0] => Array
(
[text=] => picurl
)
)
[Url] => Array
(
[0] => Array
(
[text=] => url
)
)
)
)
)
)
)
Read the xml into a DOM object, loop through it, save results into an array. Is that simple.
Simple arrays may be 2d but multi-dimensional arrays can replicate a hierarchical structure like xml very easily.
Google 'associative multi-dimensional array php' for more info.
However, as has already been stated, PHP has a built-in xml parser so there shouldn't be any need to recreate the xml within an array anyhow, let alone flatten it to a simple array.
Within PHP your array structure should resemble this:
$computers["computers"]["computer-1"]["OS"]["Name"] = "Linux";
$computers["computers"]["computer-1"]["OS"]["Age"] = "Older Than Me";
$computers["computers"]["computer-2"]["OS"]["Name"] = "Windows";
$computers["computers"]["computer-2"]["OS"]["Age"] = "Not so much";
etc...
I modified user655000's answer to be closer to how json_decode(json_encode($dom)) would format/return the data. I also made the initial array parameter optional, since it's just going to be empty anyway.
I couldn't use the decode(encode) method as there appears to be bugs in PHP's encode function, which resulted in the decode() returning null on some sample data. I tried a safer version of the encode function, but it ran out of memory.
There is a minor behavior difference. the decode(encode) method will discard any attributes (possibly children too) if there is nodeText. My method does not.
function readxml($xmlfile, $recursive = false){
$ob = simplexml_load_file($xmlfile);
//primary method
$json = json_encode($ob);
$array = json_decode($json, true);
if(is_null($array)){//backup method
$array = xml2assoc($ob);
}
return $array;
}
function xml2assoc($obj, &$arr = null) {
$children = $obj->children();//->count();
$nodes = [];
foreach ( $children as $elementName => $node ) {
if(!isset($nodes[$elementName])){
$nodes[$elementName] = 0;
}
$nodes[$elementName]++;
}
$indexes = [];
if($arr === null){
$arr = [];
}
foreach ( $children as $elementName => $node ) {
$temp = array();
$grandchildren = $node->children()->count();
//attributes
$attributes = $node->attributes();
foreach ( $attributes as $attributeName => $attributeValue ) {
$attribName = trim((string) $attributeName);
$attribVal = trim((string) $attributeValue);
$temp["#attributes"][$attribName] = $attribVal;
}
//text
$text = (string) $node;
$text = trim($text);
if (strlen($text) > 0) {
if(count($temp) == 0 && $grandchildren == 0){
$temp = $text;//discard the children/attribute data since there aren't any
} else {
$temp["NodeText"] = $text;//retain the children/attributes
}
}
//grandchildren
if($temp || is_string($temp) || $grandchildren > 0 ){
if( $nodes[$elementName] == 1 ){//only one of it's kind
$arr[$elementName] = $temp;
xml2assoc($node, $arr[$elementName]);
} else {//has multiple nodes of the same kind
if(isset($indexes[$elementName])){
$indexes[$elementName]++;
} else {
$indexes[$elementName] = 0;
}
$index = $indexes[$elementName];
$arr[$elementName][$index] = $temp;
xml2assoc($node, $arr[$elementName][$index]);
}
}
}
return $arr;
}

Categories