I have a report table. A report can have many documents (which are csv file paths). A document has many mappings.
So I initially loop my documents for a report and load the csv data using ParseCsv
foreach ($this->report->uploadedDocuments->documents as $document) {
$csv = new ParseCsv\Csv();
$csv->limit = 1;
$csv->parse(storage_path("app/" . $document->fullName));
$this->csvData = $csv->data;
}
If I output this, I see something like the following for each document (only showing one CSV row here from one document)
[
{
"id":"1",
"field1":"Some data",
"something":"Some data",
"something":"Some data",
"something":"Some data",
"something":"Some data",
}
]
Now each document has mappings so I can also do something like this
foreach ($this->report->uploadedDocuments->documents as $document) {
//Load CSV data
foreach ($document->mappings as $column) {
Log::debug(json_encode($column));
}
}
The output for the above is something like this
{
"document_id":"6434fc74-18f7-43ff-be33-3a8b0cf1cadc",
"field1":1,
"field2":null,
"field3":null,
"created_at":"2020-10-26T10:19:36.000000Z",
"updated_at":"2020-10-26T10:19:36.000000Z"
}
So from the above, I can see that field1 is located in column (index) 1 in the csv data. I can also see that this CSV does not have a field2 or field3. So my idea is to turn the original CSV data into this
[
{
"field1":"Some data",
}
]
So this is my thinking. I first need to loop the documents for the reports, load the CSV files, and read the data
foreach ($this->analysis->uploadedDocuments->documents as $document) {
$csv = new ParseCsv\Csv();
$csv->limit = 5;
$csv->parse(storage_path("app/" . $document->fullName));
if (empty($csv->data)) {
throw new Exception('Not data found in file.');
}
foreach ($csv->data as $dataKey => &$item) {
foreach ($item as $key => $value) {
//Now I have access to the keys and values from csv
}
}
}
But then how can I cross check this against my document mappings? I obviously have to loop the mappings but where should I do this? I have something which I think could work but it is very messy, and relies on my knowing the column names e.g.
foreach ($this->report->uploadedDocuments->documents as $document) {
$csv = new ParseCsv\Csv();
$csv->limit = 5;
$csv->parse(storage_path("app/" . $document->fullName));
if (empty($csv->data)) {
throw new Exception('Not data found in file.');
}
foreach ($document->mappings as $column) {
foreach ($csv->data as $dataKey => &$item) {
foreach ($item as $key => $value) {
//match the mapping column to the csv column
if ($column->fieldOne !== NULL && $key === $column->fieldOne) {
//now I have access to the values for that column
}
}
}
}
}
Would there be a better way of achieving something like this?
Thanks
I suggest the following as I understand from your question
<?PHP
$csvData = $csv->data;
$csvData = json_encode($csvData);
$arr = json_decode($csvData, true);
$field1 = array_column($arr, "field1");
print_r($field1);
?>
This line convert object into json string
$csvData = json_encode($csvData);
This line convert json string to pure array
$arr = json_decode($csvData, true);
This line extract column value from multidimensional array
$field1 = array_column($arr, "field1");
I hope this will work.
Related
I have a series of csv i need to grab from a url, they have different namings but the same extention .csv and the same exact format.
Then convert it to json.
The following is what i use for one but how to loop for any .csv?
Also once downloaded them how to merge them? So let's say we have different dates, we should expect a merge like:
{
"date": 2/4/20,
"state": "AK",...
},
{
"date": 3/4/20,
"state": "AK"...
This is the php I am using for a single csv
header('Content-Type: application/json');
if (($handle = fopen("example.com/NAME.csv", "r")) !== FALSE) {
$csvs = [];
while(! feof($handle)) {
$csvs[] = fgetcsv($handle);
}
$datas = [];
$column_names = [];
foreach ($csvs[0] as $single_csv) {
$column_names[] = $single_csv;
}
foreach ($csvs as $key => $csv) {
if ($key === 0) {
continue;
}
foreach ($column_names as $column_key => $column_name) {
$datas[$key-1][$column_name] = $csv[$column_key];
}
}
$json = json_encode($datas, JSON_PRETTY_PRINT);
fclose($handle);
print_r($json);
}
What if we have
example.com/NAME.csv
example.com/NAME2.csv
example.com/NAME3.csv
UPDATE
This is the series of csv and its data
Since there is not a way (as I know) to just scrape a site and find all files with the same extension (as per comments said and as I could not find how to using php), I ended up creating an array of names (in my case are dates) and then loop it, then the output will be a merged json:
$arr = array("04-12-2020",
"04-13-2020",
"04-14-2020",
"04-15-2020",
"04-16-2020",
"04-17-2020",
"04-18-2020",
"04-19-2020",
"04-20-2020",
"04-21-2020",
"04-22-2020",
"04-23-2020",
"04-24-2020",
"04-25-2020",
"04-26-2020",
"04-27-2020",
"04-28-2020",
"04-29-2020",
"04-30-2020",
"05-01-2020",
"05-02-2020",
"05-03-2020",
"05-04-2020",
"05-05-2020"
);
foreach($arr as $date) {
$url = $url.$date.".csv";..
// here the rest of the code as per the question
Not sure if this is the best way but works.
The result is a mega json with all dates and merged data as a single obj
I am parsing thorugh a eBay API response. I want to deliver this back to a website cleaner and easier to parse with JavaScript. I successfuly Parsed through the XML... but now turning that into JSON to resend back to the client is giving me some headaches.
NOTE: $resp is the response from eBay. It's their full length XML that is successfully parsed with the code below.
For example... $valueName could be Grade. And then I go into the next foreach loop and get the values for this. These values may be 10, 9.5, 9 etc.
Here is my PHP code.
$arrayName = array();
$arrayValue = array();
foreach($resp->aspectHistogramContainer->aspect as $name) {
$nameAspect = $name['name'];
//$arrayName["aspectName"] = $nameAspect;
foreach($name->valueHistogram as $value) {
$valueAspect = $value['valueName'];
//$arrayValue["aspectValue"] = $valueAspect;
}
//array_push($arrayName, $arrayValue);
}
echo json_encode($arrayName);
So, without me trying to create my own JSON, I am getting that I need. I echos results and it was similar to this...
NAME
----- Value
----- Value
----- Value
NAME
----- Value
NAME
etc etc
For a JSON response... Im looking for something like...
[
{
"name": "NAME",
"value": ["value", "value"]
}, {
"name": "name",
"value": ["value", "value"]
}
]
Any help and guidance would be greatly appreciated.
eBay's response is like this (there are A LOT more <aspect> and <valueHistogram>)
<getHistogramsResponse xmlns="http://www.ebay.com/marketplace/search/v1/services">
<ack>Success</ack>
<version>1.13.0</version>
<timestamp>2018-11-07T15:32:20.380Z</timestamp>
<aspectHistogramContainer>
<domainDisplayName>Baseball Cards</domainDisplayName>
<aspect name="Card Manufacturer">
<valueHistogram valueName="Ace Authentic">
<count>19</count>
</valueHistogram>
<valueHistogram valueName="American Caramel">
<count>2024</count>
</valueHistogram>
<valueHistogram valueName="APBA">
<count>10554</count>
</valueHistogram>
<valueHistogram valueName="Bazooka">
<count>8826</count>
</valueHistogram>
<valueHistogram valueName="Be A Player">
<count>17</count>
</valueHistogram>
<valueHistogram valueName="Bell Brand Dodgers">
<count>334</count>
To encode it (and assuming SimpleXML), then it's just a case of building each internal $aspect data array and then adding the values to it. I use (string) to ensure the data is not stored as a SimpleXMLElement, which can cause side effects...
$arrayName = array();
foreach($resp->aspectHistogramContainer->aspect as $name) {
$aspect = [ "name" => (string)$name['name']];
foreach($name->valueHistogram as $value) {
$aspect["value"][] = (string)$value['valueName'];
}
$arrayName[] = $aspect;
}
echo json_encode($arrayName);
with the sample XML, this gives...
[{"name":"Card Manufacturer","value":["Ace Authentic","American Caramel","APBA","Bazooka","Be A Player","Bell Brand Dodgers"]}]
Create one single array $resultArray and store values in it. By maintaining your current code structure with minimal changes, here is the updated code snippet,
$resultArray = array();
$i = 0; // Maintain Array Index value
foreach($resp->aspectHistogramContainer->aspect as $name) {
$resultArray[$i]["aspectName"] = (string)$name['name'];;
foreach($name->valueHistogram as $value) {
$resultArray[$i]["aspectValue"][] = (string)$value['valueName'];
}
$i++; // Increment array index to store next value
}
echo json_encode($resultArray);
$results = array();
// Parse the XML into a keyed array
foreach($resp->aspectHistogramContainer->aspect as $name) {
$nameAspect = (string) $name['name'];
$values = array();
foreach($name->valueHistogram as $value) {
$values[] = (string) $value['valueName'];
}
$results[$nameAspect] = $values;
}
// This keeps things simple - rewrite to the required JSON format
$outputForJSON = array();
foreach ($results as $name => $values) {
$outputForJSON[] = array(
"name" => $name,
"values" => $values
);
}
echo json_encode($outputForJSON);
When reading a sheet with phpExcel using the toArray method, hidden rows are also parsed.
Is there a method I can use before toArray to remove hidden rows?
Code so far, using Codeigniter
$this->load->library('excel');
$objPHPExcel = PHPExcel_IOFactory::load($upload_data['full_path']);
foreach ($objPHPExcel->getAllSheets() as $sheet) {
$sheets[$sheet->getTitle()] = $sheet->toArray();
}
$data = array();
foreach ($sheets['Data'] as $key => $row) {
if ($key > 0) {
$item = array();
$item['name'] = $row[1];
$item['code'] = $row[2];
$data[] = $item;
}
}
When converting the sheet to Array using PHPExcel_Worksheet::toArray you will get all of the rows, regardless if they are visible or not.
If you want to filter only the visible rows you will have to iterate over the rows and check for each of them if they are visible or not. You can check the visibility of a row using
$sheet->getRowDimension($row_id)->getVisible()
$row_id starts with 1 (and not 0), same is in excel
Here is an example of how to use it in your code. I changed a bit the way you get the Data sheet, since you don't need to iterate over the sheets, you can just get that specific sheet using the getSheetByName function.
$data_sheet = $objPHPExcel->getSheetByName('Data');
$data_array = $data_sheet->toArray();
$data = [];
foreach ($data_sheet->getRowIterator() as $row_id => $row) {
if ($data_sheet->getRowDimension($row_id)->getVisible()) {
// I guess you don't need the Headers row, note that now it's row number 1
if ($row_id > 1) {
$item = array();
$item['name'] = $data_array[$row_id-1][1];
$item['code'] = $data_array[$row_id-1][2];
$data[] = $item;
}
}
}
I have a CSV file which needs to be processed into objects.
I can open the CSV file and get all the contents that I want, no problem there. I need to match the contents of the CSV file by headers into objects. For example:
Name | Address.Street | Address.Country | Notes.Example.Value
Object->Name
Object->Address
Object->Notes
etc.
How would I handle this dynamically, not knowing what the headers are going to be beforehand?
Essentially I want to turn a string, like "Prop.Prop.Prop.etc" into a nested object.
$headers = array(); // First row of CSV.
$row = array(); // Current row of CSV.
$record = new StdClass();
foreach ($row as $key => $value) {
$properties = explode('.', $headers[$key]);
if (count($properties > 1)) {
// ???
}
else {
$record->{$properties[0]} = $value;
}
}
This should be done through a recursion. If the property you're parsing has only one level of depth, then you set the object key as a value. (which you're already doing)
If it has two or more levels, you shift the first element of the property array and recurse on the remaining levels.
Elaborating in your example:
<?php
$headers=[
'Name',
'Email',
'Address.Street',
'Address.Country',
'Notes.Example.Value'
];
$row=[
'john',
'john#gmail.com',
'beale street',
'US',
'180'
];
function setObject(&$object, $properties, $value) {
$name=array_shift($properties);
if(count($properties)===0) {
return $object->{$name} = $value;
} else {
// if this property isn't set, we declare it as a new object
if(!isset($object->{$name}) || !is_object($object->{$name})) $object->{$name} = new StdClass();
return setObject($object->{$name}, $properties,$value);
}
}
$record = new StdClass();
foreach($row as $key=>$value) {
$properties = explode('.', $headers[$key]);
setObject($record, $properties, $value);
}
echo '<pre>';
print_r($record);
echo '</pre>';
This is probably not the most elegant solution. With a bit of work you could avoid passing the object by reference back and forth.
I'm relatively new to PHP and I hope you can help me solve my problem. I am selecting out data from a database into an array for timekeeping. Ultimately, I would like to calculate the total number of hours spent on a project for a given customer.
Here is the code to populate a multi-dimensional array:
...
foreach ($record as $data) {
$mArray = array();
$name = $data['user'];
$customer = $data['customer'];
$project = $data['project'];
$hours = $data['hours'];
$mArray[$name][$customer][$project] += $hours;
}
...
I would now like to iterate over $mArray to generate an xml file like this:
...
foreach ($mArray as $username) {
foreach ($mArray[$username] as $customerName) {
foreach ($mArray[$username][$customerName] as $project ) {
echo '<'.$username.'><'.$customerName.'><'.$project.'><hours>'.
$mArray[$username][$customerName][$project].'</hours></'.$project.'>
</'.$customerName.'></'.$username.'>';
}
}
}
This nested foreach doesn't work. Can someone give me a couple of tips on how to traverse this structure? Thank you for reading!
UPDATE:
Based on the comments I've received so far (and THANK YOU TO ALL), I have:
foreach ($mArray as $userKey => $username) {
foreach ($mArray[$userKey] as $customerKey => $customerName) {
foreach ($mArray[$userKey][$customerKey] as $projectKey => $projectName) {
echo '<name>'.$userKey.'</name>';
echo "\n";
echo '<customerName>'.$customerKey.'</customerName>';
echo "\n";
echo '<projectName>'.$projectKey.'</projectName>';
echo "\n";
echo '<hours>'.$mArray[$userKey][$customerKey][$projectKey].'</hours>';
echo "\n";
}
}
}
This is now only providing a single iteration (one row of data).
Foreach syntax is foreach($array as $value). You're trying to use those values as array keys, but they're not values - they're the child arrays. What you want is either:
foreach($mArray as $username) {
foreach($username as ...)
or
foreach($mArray as $key => $user) {
foreach($mArray[$key] as ...)