Sorting An Array of Objects Based on Multiple Parameters - php

I'm trying to make an agenda of panelists for an event company - their site is made with PHP. They already have a CSV file which lists the panelist. I wrote some code so that they can just upload their CSV to their server and have it render as an table.
The csv is set up more or less like this:
Panel, Name, Last Name, Title, Company, Moderator
tuesday, John, Doe, Partner, Acme,1
tuesday, Jane, "O Reily", Partner, SkyNet,0
tuesday, Samatha, Klein, CEO, Sea World,0
tuesday, Bill, Clarke, Head of Marketing, TNT,0
wednesday, Mohammed, Algarisi, Managing Director, Cheesy Photos,1
wednesday, Tim, Draper, Founding and Managing Partner, Draper Associates,0
Anyhow, they want the panelists to be sorted alphabetically by last name, with the moderator displaying first. I'm having trouble doing this in PHP.
I'm not so used to PHP code so I'm sure I must be missing stuff, should I have set this up differently? What's the best way to sort it?
Here's basically what I did-
First I made a Panelist class:
class Panelist {
function __construct($panel, $name, $lastname, $title, $company, $moderator){
$this -> panel = $panel;
$this -> name = $name;
$this -> lastname = $lastname;
$this -> title = $title;
$this -> company = $company;
$this -> moderator = $moderator;
}
}
Then an empty array where we will store our Panelist objects
$panelists =array();
$row = 1;
//accesses our csv file from which we will get the data for the objects
if(($handle = fopen("agenda.csv", "r")) !== FALSE) {
//loops through the csv file by row
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
//skips the header (first) row
if($row == 1) {$row++; continue;}
//instanciates a Panelist object for every row in the csv file
$name = new Panelist($data[0], $data[1], $data[2], $data[3], $data[4], data[5] );
//adds object to our $panelist array
array_push($panelists, $name);
}
Then I have an output function that recieves two arguments:
1. $arr - the array where the objects are stored
2. $panelName - the name of the panel to output
function outputSpeakers($arr, $panelName){
// loops through objects in $arr
foreach($arr as $obj){
//only outputs objects with a panel value matching $panelName:
if($obj->panel == $panelName){
$name = $obj->name;
$lastname = $obj->lastname;
$title = $obj->title;
$company= $obj->company;
//lots of condition formatting stuff here that's not important such as...
if($obj->moderator == '1'){
//if the moderator is "TBA" - don't output title or company:
if($name == ' TBA'){
//format this way
} //else ...
}
}
}
}
?>
Then, in my agenda.php file I include the above class file and do:
<div class="panel-list">
<? outputSpeakers($panelist, "tuesday"); ?>
</div>
Thanks! :-)

You are getting there.
In brief, you can try this:
if ($obj->panel == $panelName) {
$result[$obj->{'last name'}] = $obj;
}
ksort($result);
But I would use a different way to create the agenda object.
<?php
class Panelist {
private $_agenda = null;
public function put($csv) {
if (!file_exists($csv)) {
return false;
}
if (($handle = fopen($csv, "r")) !== FALSE) {
$row = 1;
$count = 0;
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if ($row == 1) {
$label = $data;
$count = count($data);
} else {
if (count($data) == $count) {
for($i = 0; $i < $count; $i++) {
$agenda[$row][trim(strtolower($label[$i]))] = trim(strtolower($data[$i]));
}
}
}
$row++;
}
fclose($handle);
$this->_agenda = json_decode(json_encode($agenda));
return $this;
}
}
public function get($label, $value) {
$result = [];
if (!property_exists($this, '_agenda')) {
return false;
}
foreach ($this->_agenda as $item) {
// loop through agenda for match values by label
if (!empty($item->{$label}) && !empty($item->{'last name'}) && $item->{$label} == $value) {
// arrange $result key with last name;
$result[$item->{'last name'}] = $item;
}
}
// sort $result by key with ascending order
ksort($result);
return $result;
}
}
$panelist = new Panelist;
if ($agenda = $panelist->put("agenda.csv")->get('panel', 'tuesday')) {
foreach ($agenda as $item) {
echo $item->name . '<br>';
}
}
OUTPUT:
bill
john
samatha
jane

Related

PHP inserting value only once

I am trying to add categories in a database. Things is the script is reading the categories from a product list, therefore there are duplicate values as it'd be like reading
PRODUCT NAME - DETAIL 1 - DETAIL 2 - CATEGORY
Rinse and repeat.
I have my code down and the insert works but it stops at the first product's category value as if I put it out of my foreach loop.
<?php
$filecsv = 'pricelist.csv';
$rows = file($filecsv);
foreach($rows as $row){
$c1 = explode('|', $row);
if($c1['6'] == "not available"){
unset($c1);
continue;
}
//echo '<pre>'.print_r($c1[9], true).'</pre>';
$bool = Db::getInstance()->executeS("SELECT CASE WHEN EXISTS (SELECT * FROM b2b_category WHERE name_b2bcategory IN ('".$c1[9]."') ) THEN true ELSE false end");
foreach($bool[0] as $keyB => $valueB){
$verify = $valueB;
$count = 0;
if($valueB != 1){
Db::getInstance()->execute("INSERT INTO b2b_category (id_b2bcategory, name_b2bcategory, position_b2bcategory, active_b2bcategory) VALUES (".$count.", '".$c1[9]."', '0', '0')");
$count++;
//echo '<pre>'.print_r($valueB, true).'</pre>';
}
}
}
?>
I also want to point out my $c1 variable has multiple arrays. It's not one multi-dimensional array.
So it's like
Array {
etc
}
Array {
etc
}
Array {
etc
}
Array {
etc
}
Array {
etc
}
Since you're using MySQL, you can use on duplicate key update clause:
Db::getInstance()->execute(
"INSERT INTO b2b_category (id_b2bcategory, name_b2bcategory, position_b2bcategory, active_b2bcategory)
VALUES (".$count.", '".$c1[9]."', '0', '0')
on duplicate key update name_b2bcategory = '".$c1[9]."'"
);
You can also use a select count(1) instead of when exists:
$cnt = Db::getInstance()->executeS("SELECT count(1) FROM b2b_category WHERE name_b2bcategory IN = '".$c1[9]."'");
if($cnt[0] == 0) {
Db::getInstance()->execute("INSERT INTO b2b_category (id_b2bcategory, name_b2bcategory, position_b2bcategory, active_b2bcategory) VALUES (".$count.", '".$c1[9]."', '0', '0')");
$count++;
}
I fixed it by using this function and using my same .csv file as a multi-dimensional array. Before I couldn't operate with it due to my output being a fake array, it was recognized as string if anything. With this I could easily operate on the sub-arrays through the standard array PHP functions shortly after.
<?php
function csv_to_multidimension_array($filename, $delimiter)
{
if(!file_exists($filename) || !is_readable($filename)) {
return false;
}
$header = NULL;
$data = array();
if (($handle = fopen($filename, 'r')) !== false) {
while (($row = fgetcsv($handle, 1000, $delimiter)) !== false ) {
$data[] = $row;
}
fclose($handle);
}
return $data;
}

Incrementing the value in multidimensional array in php

I couldn't understand the multidimensional array in PHP properly. I have a CSV file having two columns as shown below:
I am trying to create an array of array, in which each key is a cataegory. However, the value of each key is an array. In this array, each key is company and value is the count of the product. See below the code:
<?php
//array contains value
function contains_value($my_array, $value_search){
foreach ($my_array as $key => $value) {
if ($value === $value_search)
return true;
}
return false;
}
//array contains key
function contains_key($my_array, $key_search){
foreach ($my_array as $key => $value) {
if ($key === $key_search)
return true;
}
return false;
}
$handle = fopen("product_list.csv", "r");
$products = array();
if ($handle) {
while (($line = fgets($handle)) !== false) {
$product = explode(",", $line);
$category = $product[0];
$company = $product[1];
if (contains_key($products, $category)) {
if (contains_value($products, $company)) {
//increase the count of category by 1
$products[$category][$company] = $products[$category][$company] + 1;
} else {
//append new company with count 1
array_push($products[$category], array(
$company,
1
));
}
} else {
//initialize new company with count 1
$products[$category] = array(
$company,
1
);
}
}
fclose($handle);
}
var_dump($products);
?>
I noticed that the var_dump($products) is not showing correction information. I am expecting following kind of result:
I haven't enough reputation to reply, but I think he need counts.
To complete the answer of Alive to Die, more something like this:
if (!array_key_exists($category, $products)) {
products[$category] = [];
}
if (!array_key_exists($company, $products[$category])) {
products[$category][$company] = 0;
}
++$results[$cataegory][$company];
But cleaner ;)
Edit:
If I remember well, his first idea was this:
$products[$category][] = $company;
The code is shorter. Maybe you can combine the two ideas.

Symfony 2: how to read excel file content usin PHPExcel

I am using PHPExcel with Symfony 2 and showing the content of an excel file like this:
$users = array();
foreach($excel as $i=>$row) {
if($i !== 1) {
array_push($users,array(
'row'=>$i,
'name'=>$row['A'],
'lastname'=>$row['B']
//...and so on
));
}
}
Question:
How can I show the content using the row name instead of $row['A']..ect?
As $row['name']... I mean the name of the excel row.
Example:
A = name B = email...and so on...
I would like to show the content like this:
$users = array();
foreach($excel as $i=>$row) {
if($i !== 1) {
array_push($users,array(
'row'=>$i,
'name'=>$row['name'],
'lastname'=>$row['surname']
//...and so on
));
}
}
I'm pretty sure that I answered this question barely a week ago.... assuming that row #1 contains your headers:
if($i == 1) {
$headers = $row;
} else {
$row = array_combine($headers, $row);
array_push($users,array(
'row'=>$i,
'name'=>$row['name'],
'lastname'=>$row['surname']
//...and so on
));
}

Pulling NHL Standings from XML Table with PHP

I'm working on a project in which I pull various statistics about the NHL and inserting them into an SQL table. Presently, I'm working on the scraping phase, and have found an XML parser that I've implemented, but I cannot for the life of me figure out how to pull information from it. The table can be found here -> http://www.tsn.ca/datafiles/XML/NHL/standings.xml.
The parser supposedly generates a multi-dimmensional array, and I'm simply trying to pull all the stats from the "info-teams" section, but I have no idea how to pull that information from the array. How would I go about pulling the number of wins Montreal has? (Solely as an example for the rest of the stats)
This is what the page currently looks like -> http://mattegener.me/school/standings.php
here's the code:
<?php
$strYourXML = "http://www.tsn.ca/datafiles/XML/NHL/standings.xml";
$fh = fopen($strYourXML, 'r');
$dummy = fgets($fh);
$contents = '';
while ($line = fgets($fh)) $contents.=$line;
fclose($fh);
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
print_r($arrOutput[0]); //This print outs the array.
class xml2Array {
var $arrOutput = array();
var $resParser;
var $strXmlData;
function parse($strInputXML) {
$this->resParser = xml_parser_create ();
xml_set_object($this->resParser,$this);
xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
xml_set_character_data_handler($this->resParser, "tagData");
$this->strXmlData = xml_parse($this->resParser,$strInputXML );
if(!$this->strXmlData) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->resParser)),
xml_get_current_line_number($this->resParser)));
}
xml_parser_free($this->resParser);
return $this->arrOutput;
}
function tagOpen($parser, $name, $attrs) {
$tag=array("name"=>$name,"attrs"=>$attrs);
array_push($this->arrOutput,$tag);
}
function tagData($parser, $tagData) {
if(trim($tagData)) {
if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] .= $tagData;
}
else {
$this->arrOutput[count($this->arrOutput)-1]['tagData'] = $tagData;
}
}
}
function tagClosed($parser, $name) {
$this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this- >arrOutput)-1];
array_pop($this->arrOutput);
}
}
?>
add this search function to your class and play with this code
$objXML = new xml2Array();
$arrOutput = $objXML->parse($contents);
// first param is always 0
// second is 'children' unless you need info like last updated date
// third is which statistics category you want for example
// 6 => the array you want that has wins and losses
print_r($arrOutput[0]['children'][6]);
//using the search function if key NAME is Montreal in the whole array
//result will be montreals array
$search_result = $objXML->search($arrOutput, 'NAME', 'Montreal');
//first param is always 0
//second is key name
echo $search_result[0]['WINS'];
function search($array, $key, $value)
{
$results = array();
if (is_array($array))
{
if (isset($array[$key]) && $array[$key] == $value)
$results[] = $array;
foreach ($array as $subarray)
$results = array_merge($results, $this->search($subarray, $key, $value));
}
return $results;
}
Beware
this search function is case sensitive it needs modifications like match to
a percentage the key or value changing capital M in montreal to lowercase will be empty
Here is the code I sent you working in action. Pulling the data from the same link you are using also
http://sjsharktank.com/standings.php
I have actually used the same exact XML file for my own school project. I used DOM Document. The foreach loop would get the value of each attribute of team-standing and store the values. The code will clear the contents of the table standings and then re-insert the data. I guess you could do an update statement, but this assumes you never did any data entry into the table.
try {
$db = new PDO('sqlite:../../SharksDB/SharksDB');
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
echo "Error: Could not connect to database. Please try again later.";
exit;
}
$query = "DELETE FROM standings";
$result = $db->query($query);
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.tsn.ca/datafiles/XML/NHL/standings.xml');
$searchNode = $xmlDoc->getElementsByTagName( "team-standing" );
foreach ($searchNode as $searchNode) {
$teamID = $searchNode->getAttribute('id');
$name = $searchNode->getAttribute('name');
$wins = $searchNode->getAttribute('wins');
$losses = $searchNode->getAttribute('losses');
$ot = $searchNode->getAttribute('overtime');
$points = $searchNode->getAttribute('points');
$goalsFor = $searchNode->getAttribute('goalsFor');
$goalsAgainst = $searchNode->getAttribute('goalsAgainst');
$confID = $searchNode->getAttribute('conf-id');
$divID = $searchNode->getAttribute('division-id');
$query = "INSERT INTO standings ('teamid','confid','divid','name','wins','losses','otl','pts','gf','ga')
VALUES ('$teamID','$confID','$divID','$name','$wins','$losses','$ot','$points','$goalsFor','$goalsAgainst')";
$result= $db->query($query);
}

CSV to Associative Array

I've seen numerous examples on how to take a CSV file and then create an associative array with the headers as the keys.
For example:
Brand,Model,Part,Test
Honda,Civic,123,244
Honda,Civic,135,434
Toyota,Supra,511,664
Where it would create an Array such as Array[$num][$key] where $key would be Brand, Model, Part, Test.
So If I wanted to access the test value "434" I would have to loop every index in the array and then ignore any Brands that were not honda, and any models that were not Civic
What I need to do is access the value most directly, instead of running through a for loop going through each $num index. I want to be able to access the value test "434" with:
Array['Honda']['Civic']['135']
or control a for statement with looping through every model Honda has... something like
foreach $model in Array['Honda']
At the very least I need to be able to go through every model given a known Brand and access all the relative info for each.
Edit:
Just to confirm I was setting this up an example. My actually data has headers like:
brand model part price shipping description footnote
Of which I need to access all the information tied to the part (price, shipping,desc, footnote)
Too many long solutions. I've always found this to be the simplest:
<?php
/* Map Rows and Loop Through Them */
$rows = array_map('str_getcsv', file('file.csv'));
$header = array_shift($rows);
$csv = array();
foreach($rows as $row) {
$csv[] = array_combine($header, $row);
}
?>
run over the csv file line by line, and insert to array like:
$array = $fields = array(); $i = 0;
$handle = #fopen("file.csv", "r");
if ($handle) {
while (($row = fgetcsv($handle, 4096)) !== false) {
if (empty($fields)) {
$fields = $row;
continue;
}
foreach ($row as $k=>$value) {
$array[$i][$fields[$k]] = $value;
}
$i++;
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fclose($handle);
}
To create an associative list array use something like:
$keys = fgetcsv($f);
while (!feof($f)) {
$array[] = array_combine($keys, fgetcsv($f));
}
And to traverse and filter by specific attributes write a function like:
function find($find) {
foreach ($array as $row) {
if (array_intersect_assoc($row, $find) == $find) {
$result[] = $row;
}
}
}
Where you would invoke it with $find = array(Brand=>Honda, Model=>Civic, Part=>135) to filter out the searched models. The other positional array structure seems not very workable, unless you only want to access the "Test" attribute.
Try this simple algorithm:
$assocData = array();
if( ($handle = fopen( $importedCSVFile, "r")) !== FALSE) {
$rowCounter = 0;
while (($rowData = fgetcsv($handle, 0, ",")) !== FALSE) {
if( 0 === $rowCounter) {
$headerRecord = $rowData;
} else {
foreach( $rowData as $key => $value) {
$assocData[ $rowCounter - 1][ $headerRecord[ $key] ] = $value;
}
}
$rowCounter++;
}
fclose($handle);
}
var_dump( $assocData);
Using fgetcsv() seems the most direct and sensible tool for the job.
csv.csv contents:
Brand,Model,Part,Test
Honda,Civic,123,244
Honda,Civic,135,434
Toyota,Supra,511,664
Code:
$assoc_array = [];
if (($handle = fopen("csv.csv", "r")) !== false) { // open for reading
if (($data = fgetcsv($handle, 1000, ",")) !== false) { // extract header data
$keys = $data; // save as keys
}
while (($data = fgetcsv($handle, 1000, ",")) !== false) { // loop remaining rows of data
$assoc_array[] = array_combine($keys, $data); // push associative subarrays
}
fclose($handle); // close when done
}
echo "<pre>";
var_export($assoc_array); // print to screen
echo "</pre>";
Output:
array (
0 =>
array (
'Brand' => 'Honda',
'Model' => 'Civic',
'Part' => '123',
'Test' => '244',
),
1 =>
array (
'Brand' => 'Honda',
'Model' => 'Civic',
'Part' => '135',
'Test' => '434',
),
2 =>
array (
'Brand' => 'Toyota',
'Model' => 'Supra',
'Part' => '511',
'Test' => '664',
),
)
Resource: http://php.net/manual/en/function.fgetcsv.php
Here is a solutions that will work by specifying a local file or URL. You can also switch the association on and off. Hopefully this helps.
class CSVData{
public $file;
public $data;
public $fp;
public $caption=true;
public function CSVData($file=''){
if ($file!='') getData($file);
}
function getData($file){
if (strpos($file, 'tp://')!==false){
copy ($file, '/tmp/csvdata.csv');
if ($this->fp=fopen('/tmp/csvdata.csv', 'r')!==FALSE){
$this->readCSV();
unlink('tmp/csvdata.csv');
}
} else {
$this->fp=fopen($file, 'r');
$this->readCSV();
}
fclose($this->fp);
}
private function readCSV(){
if ($this->caption==true){
if (($captions=fgetcsv($this->fp, 1000, ","))==false) return false;
}
$row=0;
while (($data = fgetcsv($this->fp, 1000, ",")) !== FALSE) {
for ($c=0; $c < count($data); $c++) {
$this->data[$row][$c]=$data[$c];
if ($this->caption==true){
$this->data[$row][$captions[$c]]=$data[$c];
}
}
$row++;
}
}
}
Try this usage:
$o=new CSVData();
$o->getData('/home/site/datafile.csv');
$data=$o->data;
print_r($data);
Here is my solution, similar to others stated but uses a while loop with fgetcsv, and uses a counter and array_combine to set the first row as the keys.
$rownum = 0;
while (($row = fgetcsv($openedFile, 1000, ',')) !== FALSE) {
if ($rownum > 0) {
$row = array_combine($importarray[0], $row);
}
array_push($importarray, $row);
$rownum++;
}
array_shift($importarray);

Categories