Multi-dimension array value sorting in PHP - php

I am building up an array with a set of database fields with information about table, actual field name and descriptive field name as a multi-dimensional array. Here is what it currently looks like:
$Fields['User']['ID'] = "User ID";
$Fields['User']['FirstName'] = "First Name";
$Fields['Stats']['FavouriteOrder'] = "Favourite Item Ordered";
$Fields['Geographic']['Location'] = "Current Location";
$Fields['Geographic']['LocationCode'] = "Current Location Code";
Okay, this is fine, but I am piping this into a system that allows exporting of selected fields, and in the end I want to foreach() through the different levels, extract the data and then ultimately have all the descriptive fields to be displayed sorted alphabetically using their descriptive name. So ultimately in the order: Current Location, Current Location Code, Favorite Item Ordered, First Name then User ID - obviously keeping index associations.
I can't use usort() and I can't use array_multisort()... or maybe I can and I just don't know how. usort() seems to need a key to sort by, but I have variable keys. array_multisort() just seems to do the same as sort() really.

This is for a 2D array only. Not the most elegant piece of code I've written, but it works...
foreach($Fields as $key=>$var) {
ksort($var);
$Fields[$key]=$var;
}
ksort($Fields);

Let me rather give a real-life data example, as opposed to fake data because the fake data nearly confused me. So, fake data is commented.
/*
$Fields['User']['ID'] = "User ID";
$Fields['User']['FirstName'] = "First Name";
$Fields['Stats']['FavouriteOrder'] = "Favourite Item Ordered";
$Fields['Geographic']['Location'] = "Current Location";
$Fields['Geographic']['LocationCode'] = "Current Location Code";
*/
$Fields['Product']['ReferenceNumber'] = "Product Reference Number";
$Fields['Product']['Halaal'] = "Halaal Status";
$Fields['Product']['Kosher'] = "Kosher Status";
$Fields['Product']['KosherType'] = "Kosher Type";
$Fields['Product']['CuringSalts'] = "Curing Salts Status";
$Fields['Product']['ProductVisibility'] = "Product Visibility";
$Fields['Product']['ProductStatus'] = "Product Status";
$Fields['Product']['PackBarCode'] = "Barcode";
$Fields['Product']['ProductDescription'] = "Product Description";
$Fields['Pack']['PackSize'] = "Pack Size";
$Fields['Pack']['PackSizeNumeric'] = "Numeric Pack Size";
$Fields['Allergens']['ContainsNuts'] = "Product Contains Nuts";
foreach ($Fields as $key => $value) {
ksort($value);
$Fields[$key] = $value;
}
ksort($Fields);
I'm having one of 'those' Fridays... print_r($Fields) reveals that the keys are being sorted and values are associated, but it's still sorting by the keys and not the end value.
It's almost like i need a reverse sorting system which checks all values first, sorts them and then says 'okay where do you belong ... ah you belong to FieldX in Table Y'
I was hoping there was a sneaky clever way to do it, perhaps there is, but I guess I'll write a function to parse through the data, write a reversed array and then re-write the original in value-order. Hectically inefficient, but it'll do.
Still open to better suggestions though!

I literally had to work this out yesterday for a project I was working on - here's my code:
Resource array looks like this:
$resource[$count]['title']
$resource[$count]['filepath']
$resource[$count]['filename']
$resource[$count]['taxonomy'][0]
A couple of sort functions to sort by title ASC or DESC
function compare_asc($a, $b) {
return strcmp($a['title'], $b['title']);
}
function compare_desc($a, $b) {
if ($a['title'] == $b['title']) {
return 0;
}
return ($a['title'] > $b['title']) ? -1 : 1;
//return strcmp($a['title'], $b['title']);
}
And finally use usort to do the dirty before you loop through $resource and output whatever it is you need.
usort($resource, "compare_asc");

Okay, it's not elegant at all. So I don't encourage using this and will look for a better way down the line. But here's the solution I've got that works with the examples above and below.
Unfortunately, the way I want it to be, the array HAS to contain a preceding 'ordering' number so, I suppose it's a fail on my part from the very beginning. But it works now.
$TempDescArray = array();
$TempFieldArray = array();
$TempTableArray = array();
$Pointer = 0;
foreach ($Fields as $Table => $FieldsArray) {
foreach ($FieldsArray as $Field => $Description) {
$TempDescArray[$Pointer] = $Description;
$TempFieldArray[$Pointer] = $Field;
$TempTableArray[$Pointer] = $Table;
$Pointer++;
}
}
asort($TempDescArray);
$Fields = array();
$Pointer2 = 0;
foreach ($TempDescArray as $Pointer => $Description) {
$Fields[$Pointer2][$TempTableArray[$Pointer]][$TempFieldArray[$Pointer]] = $Description;
$Pointer2++;
}

Related

PHP Function to search values in database and replace in a loop

I am building a web scraper in PHP and I am not so experimented with all this stuff. What I am trying to achieve is as following:
Split an array of values into strings using foreach
Search any value in a predefined MYSQL table. If value is identical with one of the defined ones, it should be replaced. Otherwise it should remain the same
Put the new values back into an array
Below is my snippet. Basic structure of database is "ID, Marime, Inlocuire". "Marime" is the column to search on, and "Inlocuire" is the column to replace value with.
foreach ($marimi as $marime) {
$sizes[]=trim(strtok($marime->innertext, '-'));
$newArray = array_filter($sizes, 'myFilter');
foreach ($newArray as $marimeFixa) {
$marimeDefinita = $conn->query("SELECT * FROM oc_1_tabelmarimi WHERE Marime = '$marimeFixa'");
if($marimeDefinita->num_rows == 0) {
$marimeFixa = $marimeFixa;
} else {
$marimeFixa = $marimeDefinita['Inlocuire'];
}
$arrayMarimi[] = $marimeFixa;
}
print_r($arrayMarimi);
}
However this doesn't seem to work. Any help is greatly appreciated. Thanks!
try:
$marimeDefinita = $marimeDefinita->fetch_assoc();
before if($marimeDefinita->num_rows == 0) {
or
$marimeFixa = $marimeDefinita->Inlocuire;

php jumping to previous statement inside a loop

So basically i'm trying to create a complex timetable and i have these two methods that each perform a different check function for me:
Checks if i have a unique array
function tutorAllot($array,$check,$period){
//check for clashes and return non colliding allotment
shuffle($array);
$rKey = array_rand($array);
if(array_key_exists($array[$rKey]['teacher_id'], $check[$period])) {
return $this->tutorAllot($array,$check,$period);
}
return $tutor = array($array[$rKey]['teacher_id'] => $array[$rKey]['subject_code']);
}
checks that each subject does not appear more than twice in a day
function checkDayLimit($data,$check){
//check double day limit
$max = 2;
$value = array_values($check);
$tempCount = array_count_values($data);
return (array_key_exists($value[0], $tempCount) && $tempCount[$value[0]] <= $max) ? true : false;
}
I'm calling the functions from a loop and populating timetable array only if all conditions area satisfied:
$outerClass = array();
foreach ($value as $ky => $val) {
$innerClass = array(); $dayCount = array();
foreach ($periods[0] as $period => $periodData) {
$innerClass[$period] = array();
if(!($periodData == 'break')){
$return = $this->Schedule->tutorAllot($val,$clashCheck,$period);
if($return){
//check that the returned allocation hasnt reached day limit
if($this->Schedule->checkDayLimit($dayCount,$return)){
$innerClass[$period] += $return;
$clashCheck[$period] += $return;
}else{
}
}
}else{
$innerClass[$period] = '';
}
}
//debug($innerClass);
$outerClass[$ky] = $innerClass;
}
My requirements
If the checkDayLimit returns false , i want to go back and call tutorAllot function again to pick a new value.
I need to do this without breaking the loop.
I was thinking maybe i could use goto statement but only when am out of options.
Is there a way i can achieve this without using goto statement.
PHP v5.5.3 Ubuntu
Your architecture seems overly complex. Instead of
pick at random >> check limit >> if at limit, go to re-pick...
Why not incorporate both checks into a single function? It would
Filter out data that is not eligible to be picked, and return an array of legitimate choices
Pick at random from the safe choices and return the pick
addendum 1
I don't think there is any need for recursion. I would use array_filter to pass the data through a function that returns true for eligible members and false for the rest. I would then take the result of array_map and make a random selection from it

convert mysql result to json with correct types

I know how to get a mysql-row and convert it to json:
$row = mysqli_fetch_assoc(mysqli_query($db, "SELECT * FROM table WHERE id=1"));
echo json_encode($row); // it's an ajax-call
but:
the db-row has different types like int, float, string.
by converting it using json_encode() all results are strings.
Is there a better way to correct the types than this:
$row['floatvalue1'] = 0+$row['floatvalue1'];
$row['floatvalue2'] = 0+$row['floatvalue2'];
$row['intvalue1'] = 0+$row['intvalue1'];
I would like to loop through the keys and add 0 because:
first coding rule: DRY - dont repeat yourself
but i can't because:
row has also other types than numbers (string, date)
there are many columns
design is in dev, so columns-names often changes
Thanks in advance and excuse my bad english :-)
EDIT (to answer the comment-question from Casimir et Hippolyte):
I call this php-code using ajax to get dynamically sql-values. in my javascript-code i use the results like this:
result['intvalue1'] += 100;
lets say the json-result of intval1 is 50, the calculated result is:
"50100", not 150
The code below is just a proof of concept. It needs encapsulation in a function/method and some polishing before using it in production (f.e. call mysqli_fetch_field() in a loop and store the objects it returns before processing any row, not once for every row).
It uses the function mysqli_fetch_field() to get information about each column of the result set and converts to numbers those columns that have numeric types. The values of MYSQLI_TYPE_* constants can be found in the documentation page of Mysqli predefined constants.
// Get the data
$result = mysqli_query($db, "SELECT * FROM table WHERE id=1");
$row = mysqli_fetch_assoc($result);
// Fix the types
$fixed = array();
foreach ($row as $key => $value) {
$info = mysqli_fetch_field($result);
if (in_array($info->type, array(
MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_INT24,
MYSQLI_TYPE_LONG, MYSQLI_TYPE_LONGLONG,
MYSQLI_TYPE_DECIMAL,
MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE
))) {
$fixed[$key] = 0 + $value;
} else {
$fixed[$key] = $value;
}
}
// Compare the results
echo('all strings: '.json_encode($row)."\n");
echo('fixed types: '.json_encode($fixed)."\n");
something like
$row['floatvalue1'] = reset( sscanf ( $row['floatvalue1'] , "%f" ));
$row['floatvalue2'] = reset( sscanf ( $row['floatvalue2'] , "%f" ));
$row['intvalue1'] = reset( sscanf ( $row['intvalue1'] , "%d" ));
json_encode($row);
If you're simply trying to make sure that your values are operable with respect to their type, you need to first cast their type correctly.
Unless you need them server-side, I would just pass-on the json directly to the front-end and do the work there.
In Javascript, you could make an attempt at casting the numbers like so:
function tryNumber(string){
return !isNaN( parseInt(string) ) ? parseInt(string) : string;
}
function tryDate(string){
return !isNaN( new Date(string).getTime() ) ? new Date(string) : string;
}
tryNumber('foo'); // "hello"
tryNumber('24'); // 24
tryDate('bar'); // "bar"
tryDate('December 17, 1995'); // "Sun Dec 17 1995 00:00:00 GMT+0000 (GMT)"
These two lines attempt to cast the values as a Date/Number. If they can't be cast, they will remain String's.
A MySQLi OO version based on #axiac's answer, that produces a JSON array ($jsnAll) containing all records. In this code snippet, the method FixSQLType is called to fix a row. Note, it should be wrapped in a try{}catch{} block and "objMySQLi" has already been instantiated:
$lcAllRows = array();
// Make an SQL SELECT statement
$SQL = "SELECT * FROM $lcName WHERE $lcWhere";
// Run the query
$this->sqlResult = $this->objMySQLi->query($SQL);
// Fetch the result
while( $row = $this->sqlResult->fetch_assoc()){
$lcCount = count($lcAllRows) ;
// Call to fix, row
$fixedRow = $this->FixSQLType($row);
$lcAllRows[$lcCount]= $fixedRow;
}
$jsnAll = json_encode($lcAllRows);
The FixSQLType method. This is almost identical to #axiac's answer, except for the call to $this->sqlResult->fetch_field_direct($i). "fetch_field" seemed to get itself lost, using "fetch_field_direct" overcame that problem.
private function FixSQLType($pRow){
// FROM https://stackoverflow.com/a/28261996/7571029
// Fix the types
$fixed = array();
$i = 0;
foreach ($pRow as $key => $value) {
$info = $this->sqlResult->fetch_field_direct($i);
$i++;
if (in_array($info->type, array(
MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_INT24,
MYSQLI_TYPE_LONG, MYSQLI_TYPE_LONGLONG,
MYSQLI_TYPE_DECIMAL,
MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE
))) {
$fixed[$key] = 0 + $value;
} else {
$fixed[$key] = $value;
}
}
return $fixed;
}

Fetch result from database to multiple variables

I have a site were the user fills a form and all data is stored in a database, when the user enter his/hers page all the added data is visible. Today I´m doing this but in a lot of code rows and there is for sure a much smoother way to do this.
Here´s a look of how I have done it today:
$query = mysqli_query($dbhandle, "SELECT * FROM ..."); // ... added now
$row = mysqli_fetch_assoc($query);
$m0 = $row['m1'];
$m1 = $row['m2'];
$m2 = $row['m3'];
$m3 = $row['m4'];
...
$m47 = $row['m48'];
$firstPlace = $row['firstPlace '];
$secondPlace = $row['secondPlace '];
$thirdPlace = $row['thirdPlace '];
$fourthPlace= $row['fourthPlace'];
As you can see there are a lot of rows of code. What I would like to do is to loop through my query and then add the right value in the database to the right value in the form.
Appreciate help.
There definitely are many alternative (and in every possible sense of the word) better ways to go about your business.
For a kickoff: ask yourself what an array actually is. An array is a collection of data. You store them together because one value of that array in itself doesn't mean much. The data in an array belongs together. Why then, assign it to individual variables in the first place?
Of course, your $row array has keys like $row['m1'], which you assign to a variable called $m0. so the names of the fields in the database don't quite match the names your code uses. That's something that you can, quite easily, fix by changing your query: use aliasses for those fields:
SELECT m1 as m0, ... FROM
Now your array will have a key called m0, instead of m1. This reduces the rest of your code down to:
$row = mysqli_fetch_assoc($query);
echo 'M0: ', $row['m0'];//<-- use m0 value here.
Alternatively, you could use a second array that maps these field-names to the name you want to use in your code:
$map = array(
'm0' => 'm1'
);
echo 'M0: ', $row[$map['m0']];//use value of m0, which is the actual key if the $row array
Still, if you are hell-bound on unmaintainable, messy, error-prone and just awful code, you could use variable variables:
foreach ($row as $key => $value)
{
$$key = $val;
}
Note the double $ in $$key. This is like saying "the variable that is called whatever the value of $key is". If $key is firstname, the code above evaluates to $firstname = $value. But whatever you do: forget this is possible. It's like an enema: yes, it's possible, but you don't want one if you can avoid it. And in this case, you clearly can avoid it.
Loop through the $row var grabbing the key and value. If key starts with "m" followed by a 1 or 2 digit number, get the number, subtract one, concatenate it with "m", and assign the value. Otherwise just interpolate key into variable name and assign value.
foreach ( $row as $key => $value ) {
if ( preg_match('/^m(\d{1,2})/', $key, $matches) ) {
${'m' . ($matches[1] - 1)} = $value;
}
else { $$key = $value; }
}
In the above example, $row['m1'] value gets assigned to var $m0, and $row['firstPlace'] to var $firstPlace, etc.

PHP Order in alphabetical order

I'm trying to make a simple alphabetical list to order items in my database. The thing I can't figure out is how to actually list it.
I would like it to be the same format as you have on miniclip.com
Here's an image
I looked around, but couldnt find an answer really.
(I would like it to finish even at the end of each vertical column, except the last one for sure)
Any help would be welcome!
In MySQL:
SELECT * FROM table ORDER BY name ASC
In PHP:
$fruits = array("lemon", "orange", "banana", "apple");
sort($fruits);
foreach ($fruits as $key => $val) {
echo "fruits[" . $key . "] = " . $val . "\n";
}
fruits[0] = apple
fruits[1] = banana
fruits[2] = lemon
fruits[3] = orange
Assuming that your result set already is sorted by using the ORDER BY clause, to group the results by their first character you just need to remember the first character of the previous entry and print out the first character of the current entry if they are different. So:
$prevLabel = null;
while ($row = mysql_fetch_assoc($result)) {
$currLabel = strtoupper(substr($row['name'], 0, 1));
if ($currLabel !== $prevLabel) {
echo $currLabel;
$prevLabel = $currLabel;
}
echo $row['name'];
}
This will print the first character as a label for each group that’s members have the same first character.
He doesn't seem to have an issue with the storting, but doing the column format and headers for each new letter.
Suppose $arr contains your alphabetically sorted list with numeric keys. each element has indexes 'name' and 'link'. This should be pretty safe assumption for data from a SQL query.
$firstLetter = -1;
$desiredColumns = 4; //you can change this!
$columnCount = (count($arr)+27)/$desiredColumns+1;
echo "<table><tr><td>";
foreach($arr as $key => $cur)
{
if ($key != 0 && $key % desiredColumns == 0) echo "</td><td>";
if ($cur['name'][0] !== $firstLetter)
{
echo "<strong>$firstLetter</strong> <br />"; $firstLetter = $cur['name'][0];
}
echo "".$cur['name']."<br />";
}
echo "</td><tr></table>";
You'll have to treat numbers as a special case, but this is the idea. If you are using a template engine there are obviously better ways of doing this, but I figure you would have mentioned that. This is a rough sketch, making pretty HTML isn't my thing.
--Query-- get table into $arr. I can't see your tables obviously, Im making assumptions if names nad stuff so you'll need to verify or change them
$sql = "SELECT * FROM table T ORDER BY name";
$conn = //you should have this
$res = mysql_query($sql, $conn);
$arr = array();
while($row = mysql_fetch_assc($res)
$arr[] = $row;
// start above code here. This isn't safe for empty query responses or other error but it works
I presume you're using MySQL (or another SQL) database, in which case you should simply retrieve the data in the required order using a SORT BY clause on the lookup SELECT. (Sorting this PHP is trivial via the sort function, but it makes sense to get the database to do this - that's pretty much what it's for.)
In terms of balancing the output of each of the columns, you could get a COUNT of the required rows in your database (or simply use the count of the resulting PHP array of data) and use this to ensure that the output is balanced.
As a final thought, if this is going to be output on a per-page basis, I'd highly recommend generating it into a static file when the structure changes and simply including this static file as a part of the output - generating this on the fly is needlessly resource inefficient.
The mysql option mentioned above is definitely the best bet. If the data comes out of the DM in order, that's the simplest way to go.
Your next option might be to look at the
asort and ksort functions in PHP to find the exact one you're looking for.
http://www.php.net/manual/en/array.sorting.php
How are you pulling the data?
<?php
$result = mysql_query("SELECT titles FROM gamelist ORDER BY title ASC");
while ($row = mysql_fetch_assoc($result)) {
echo "{$result['title']}<br/>";
}
?>
There are two ways to do it.
You could use your database and use the 'order' clause to pull them by a specific field alphabetically.
You could also use either a key sort or value sort on a PHP array.
The PHP functions are sort($array) and ksort($array).
http://php.net/manual/en/function.sort.php
http://php.net/manual/en/function.ksort.php
<?php
$list = $your_list_array_from_database
//if you need info on how to do this, just let me know
sort($list);
foreach($list as $item) {
echo $item;
}
?>
I found this post and had the same problem. I used the code below to output a list by category name with a header equal to the first letter. In my database table (category) I have name and category_letter. So, name = football and category_list = 'F'.
<section>
<?php
try {
$cats_sql = $dbo->prepare("SELECT name, category_list, FROM category WHERE category_list REGEXP '^[A-Z#]' GROUP BY category_list ASC");
$cats_sql->execute();
$results_cats = $cats_sql->fetchAll();
} catch(PDOException $e) {
include('basehttp/error');
}
$array_cats = $results_cats;
if(is_array($array_cats)) {
foreach($array_cats as $row_cats) {
$cat_var = $row_cats[category_list]; // Each Category list title
?>
<aside>
<h1><a name=""><? echo $cat_var ?></a></h1>
<?php
try {
$search_sql = $dbo->prepare("SELECT name, category_list FROM category WHERE category_list=:cat_var ORDER BY name ASC"); // Pulling a list of names for the category list
$search_sql->bindParam(":cat_var",$cat_var,PDO::PARAM_STR);
$search_sql->execute();
$results_search = $search_sql->fetchAll();
} catch(PDOException $e) {
include('basehttp/error');
}
$array_search = $results_search;
if(is_array($array_search)) { // Output list of names which match category
foreach($array_search as $row_search) {
?>
<h2><?php echo $row_search[name]; ?></h2>
<br class="clear">
<?php
}
}
?>
</aside>
<br class="clear">
<?php
}
}
?>
</section>
Its actually Simple....I did similar thing for my project once. I had to pull out all music albums name and categorize them in alphabetical order.
In my table, "album_name" is the column where names are stored.
$sql= "select * from album_table order by album_name ASC";
$temp_char= ""; // temporary variable, initially blank;
using while loop, iterate through records;
while($row= $rs->fetch_assoc())
{
$album_name= $row['album_name'];
$first_char_of_albm= $album_name[0]; // this will store first alphabet;
$first_char_of_albm= strtoupper($first_char_of_albm); // make uppercase or lower as per your needs
if($temp_char!=$first_char_of_albm)
{
echo $first_char_of_albm;
$temp_char= $first_char_of_albm; // update $temp_char variable
}
}
That's it....
I am posting my answer to this old question for 3 reasons:
You don't always get to write your queries to MySQL or another DBMS, as with a web service / API. None of the other answers address PHP sorting without query manipulation, while also addressing the vertical alphabetical sort
Sometimes you have to deal with associative arrays, and only a couple other answers deal with assoc. arrays. BTW, my answer will work for both associative and indexed arrays.
I didn't want an overly complex solution.
Actually, the solution I came up with was pretty simple--use multiple tags with style="float:left", inside of a giant table. While I was sceptical that having multiple tbody tags in a single table would pass HTML validation, it in fact did pass without errors.
Some things to note:
$numCols is your desired number of columns.
Since we are floating items, you may need to set the width and min-width of parent elements and/or add some <br style="clear: both" />, based on your situation.
for alternative sorting methods, see http://php.net/manual/en/array.sorting.php
Here's my full answer:
function sortVertically( $data = array() )
{
/* PREPARE data for printing */
ksort( $data ); // Sort array by key.
$numCols = 4; // Desired number of columns
$numCells = is_array($data) ? count($data) : 1 ;
$numRows = ceil($numCells / $numCols);
$extraCells = $numCells % $numCols; // Store num of tbody's with extra cell
$i = 0; // iterator
$cCell = 0; // num of Cells printed
$output = NULL; // initialize
/* START table printing */
$output .= '<div>';
$output .= '<table>';
foreach( $data as $key => $value )
{
if( $i % $numRows === 0 ) // Start a new tbody
{
if( $i !== 0 ) // Close prev tbody
{
$extraCells--;
if ($extraCells === 0 )
{
$numRows--; // No more tbody's with an extra cell
$extraCells--; // Avoid re-reducing numRows
}
$output .= '</tbody>';
}
$output .= '<tbody style="float: left;">';
$i = 0; // Reset iterator to 0
}
$output .= '<tr>';
$output .= '<th>'.$key.'</th>';
$output .= '<td>'.$value.'</td>';
$output .= '</tr>';
$cCell++; // increase cells printed count
if($cCell == $numCells){ // last cell, close tbody
$output .= '</tbody>';
}
$i++;
}
$output .= '</table>';
$output .= '</div>';
return $output;
}
I hope that this code will be useful to you all.

Categories