Stable sort with multiple conditions - php

Hi i am facing problem in sorting multidimensional array by different key,such as by date, by category, by weight in any specific order.
I can not order these array by mysql order by feature as i have to implement a tough business logic on mysql output array (data)..
After implementing business logic i found following type of array that need to be sorted by
date asc,category desc,weight asc.
array have size of 10000 or more.
i have already used usort function but it can't resolve issue of fixed ordering in case of same value of sorting elements.
plz help.
Array
(
[0] => Array
(
[categorie] => xyz
[date] => 2012-12-08 19:30
[weight] => 3
[row_id] => 125812
[searchtype] => show
[uitgespeeld] => 0
)
[1] => Array
(
[categorie] => show
[date] => 2012-12-10 20:15
[weight] => 3
[row_id] => 125816
[searchtype] => show
[uitgespeeld] => 0
)
[2] => Array
(
[categorie] => abc
[date] => 2012-12-13 20:30
[weight] => 3
[row_id] => 119151
[searchtype] => show
[uitgespeeld] => 0
)
.......
)
Code i have used for sorting.
usort($temp_group_data, array('className','cmp_weight'));
usort($temp_group_data, array('className','cmp_date'));
function cmp_weight($a, $b) {
if (($a['weight']==$b['weight']) ) {
return 0;
} else if ($a['weight'] >$b['weight']) {
return -1;
} else {
return 1;
}
}
function cmp_date($a, $b) {
if (($a['date']==$b['date']) ) {
return 0;
} else if (strtotime($a['date']) >strtotime($b['date'])) {
return -1;
} else {
return 1;
}
}

You have to do it in one function, now second sorting overwrites changes made in first.
function multicompare($a,$b){
$criteria = array(
'date' => 'asc',
'category' => 'desc',
'weight' => 'asc'
);
foreach($criteria as $what => $order){
if($a[$what] == $b[$what]){
continue;
}
return (($order == 'desc')?-1:1) * strcmp($a[$what], $b[$what]);
}
return 0;
}

The problem is two-fold, judging by the last part of your question:
All conditions must be evaluated at the same time rather than consecutively.
You need stable sorting to retain the ordering in case two values are the same (i.e. the original order)
Both steps in one goes like this; first you "decorate" the array using the index in which they appear in the original array:
foreach ($a as $key => &$item) {
$item = array($item, $key); // add array index as secondary sort key
}
usort($a, 'mysort'); // sort it
// undecorate
foreach ($a as $key => &$item) {
$item = $item[0]; // remove decoration from previous step
}
And here's the all-in-one sorting function:
function mysort($a, $b)
{
if ($a[0]['date'] != $b[0]['date']) {
return $a[0]['date'] < $b[0]['date'] ? -1 : 1; // ASC
} elseif ($a[0]['category'] != $b[0]['category']) {
return $a[0]['category'] < $b[0]['category'] ? 1 : -1; // DESC
} elseif ($a[0]['weight'] != $b[0]['weight']) {
return $a[0]['weight'] < $b[0]['weight'] ? -1 : 1; // ASC
} else {
return $a[1] < $b[1] ? -1 : 1; // ASC
}
}

Related

PHP: How to move array item to the first place without changing its key if value is numeric?

Here is my array:
$arrayA = array(0 => "someString",
1 => "otherString",
2 => "2017",
3 => "anotherString",
4 => "2016");
My goal is to to find the first item that has a numeric value (which would be "2017") and place it first in the array, without changing its original key and keeping the others in the same order.
So I want:
$arrayA = array(2 => "2017",
0 => "someString",
1 => "otherString",
3 => "anotherString",
4 => "2016");
I tried uasort() php function and it seems the way to do it, but I could not figure out how to build the comparison function to go with it.
PHP documentation shows an example:
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
But, WHO is $a and WHO is $b?
Well, I tried
function my_sort($a,$b) {
if ($a == $b ) {
return 0;
}
if (is_numeric($a) && !is_numeric($b)) {
return -1;
break;
}
}
But, of course, I am very far from my goal. Any help would be much appreciated.
You don't need to sort per se. Once you find the element in question, you can simply push it onto the front of the array with the + operator:
foreach ($arrayA as $k => $v) {
if (is_numeric($v)) {
$arrayA = [$k => $v] + $arrayA;
break;
}
}
print_r($arrayA);
Yields:
Array
(
[2] => 2017
[0] => someString
[1] => otherString
[3] => anotherString
[4] => 2016
)

Sorting of multidimensional array with with numbers and letters

How to sort multidimensional array. This is what my array looks like
[0] => Array
(
[id] => 1
[title] => 3A
[active] => 1
)
[1] => Array
(
[id] => 1
[title] => A
[active] => 1
)
[2] => Array
(
[id] => 1
[title] => 2A
[active] => 1
)
[3] => Array
(
[id] => 1
[title] => B
[active] => 1
)
I have tried several usort methods, but cannot seem to get this to work. I am needing the array sorted so that it will sort by numeric then by alpha numeric like so: A,B,2A,3A.
I am not sure if this would be possible without adding a position field to dictate what order the titles are suppose to be in, or am I missing something here?
You can build a "key" for each item where the digit part is padded on the left with 0s, this way, the sort function can perform a simple string comparison:
$temp = [];
foreach ($arr as $v) {
$key = sscanf($v['title'], '%d%s');
if (empty($key[0])) $key = [ 0, $v['title'] ];
$key = vsprintf("%06d%s", $key);
$temp[$key] = $v;
}
ksort($temp);
$result = array_values($temp);
demo
This technique is called a "Schwartzian Transform".
As #Kargfen said, you can use usort with your custom function. Like this one :
usort($array, function(array $itemA, array $itemB) {
return myCustomCmp($itemA['title'], $itemB['title']);
});
function myCustomCmp($titleA, $titleB) {
$firstLetterA = substr($titleA, 0, 1);
$firstLetterB = substr($titleB, 0, 1);
//Compare letter with letter or number with number -> use classic sort
if((is_numeric($firstLetterA) && is_numeric($firstLetterB)) ||
(!is_numeric($firstLetterA) && !is_numeric($firstLetterB)) ||
($firstLetterA === $firstLetterB)
) {
return strcmp($firstLetterA, $firstLetterB);
}
//Letters first, numbers after
if(! is_numeric($firstLetterA)) {
return -1;
}
return 1;
}
This compare-function is just based on the first letter of your titles, but it can do the job ;-)
You can resolve that problem with help of usort and custom callback:
function customSort($a, $b)
{
if ($a['id'] == $b['id']) {
//if there is no number at the beginning of the title, I add '1' to temporary variable
$aTitle = is_numeric($a['title'][0]) ? $a['title'] : ('1' . $a['title']);
$bTitle = is_numeric($b['title'][0]) ? $b['title'] : ('1' . $b['title']);
if ($aTitle != $bTitle) {
return ($aTitle < $bTitle) ? -1 : 1;
}
return 0;
}
return ($a['id'] < $b['id']) ? -1 : 1;
}
usort($array, "customSort");
At first the function compares 'id' values and then if both items are equal it checks 'title' values.

Sort Date array from txt file

I have a .txt file that store all the information from my form. And I want to have 1 php that display status from the oldest date to today's date.
This is some example of my txt format:
ID4494 (tab("\t")) 12/02/2008 (tab("\t")) ANJAY
ID4496 (tab("\t")) 14/04/2009 (tab("\t")) SONJA
ID4499 (tab("\t")) 19/03/2014 (tab("\t")) BRIAN
and this is my php file
$myfilename = "../idfile/digid.txt";
if( file_exists($myfilename) && filesize($myfilename) > 0 ) {
$digArray = file($myfilename, FILE_IGNORE_NEW_LINES);
sort($digArray);
foreach($digArray as $dig) {
$oneDigArray = explode("\t", $dig);
echo "<p>$oneDigArray[0] - $oneDigArray[1] - $oneDigArray[2]</p>";
}
} else {
echo "There is no Digital ID.";
}
I tried to use sort ($digArray[1]); since the date is array no. 1. But it wont work.
Any suggestion?
Thank you.
You can use PHP's usort function and supply your own comparison function. Like this:
function cmp($a, $b)
{
return ($a[1] > $b[1]) ? 1 : -1; // Ascending order
// OR
return ($a[1] < $b[1]) ? 1 : -1; // Descending order
}
usort($digArray, "cmp");
The above will sort your array by date. Here is a test I did.
Test:
$digArray = array(
array(
"ID4496", "14/04/2009", "SONJA"
),
array(
"ID4499", "19/03/2014", "BRIAN"
),
array(
"ID4494", "12/02/2008", "ANJAY"
),
);
function cmp($a, $b)
{
return ($a[1] > $b[1]) ? 1 : -1; // Ascending order
}
usort($digArray, "cmp");
print_r($digArray);
Output:
Array (
[0] => Array ( [0] => ID4494 [1] => 12/02/2008 [2] => ANJAY )
[1] => Array ( [0] => ID4496 [1] => 14/04/2009 [2] => SONJA )
[2] => Array ( [0] => ID4499 [1] => 19/03/2014 [2] => BRIAN )
)

sorting array based on inner-array key-value [duplicate]

This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 7 years ago.
I have an array like one mentioned below
Array
(
[6] => Array
(
[name] => Extras
[total_products] => 0
[total_sales] => 0
[total_affiliation] => 0
)
[5] => Array
(
[name] => Office Products
[total_products] => 7
[total_sales] => 17
[total_affiliation] => 8
)
[1] => Array
(
[name] => Hardware Parts
[total_products] => 6
[total_sales] => 0
[total_affiliation] => 0
)
)
Right now, order is: Extras, Office Products, Hardware Parts
I want to sort main array in such as way that it is order by total_sales of inner-array in desc order
so order will be: Office Products, Extras, Hardware Parts
Any help guys
PHP 5.3:
usort($array, function ($a, $b) { return $b['total_sales'] - $a['total_sales']; });
PHP 5.2-:
usort($array, create_function('$a,$b', 'return $b["total_sales"] - $a["total_sales"];'));
Use a custom function and usort:
<?php
function custom_sale_sort($a, $b)
{
if ($a['total_sales'] < $b['total_sales'])
return 1;
elseif ($a['total_sales'] == $b['total_sales'])
return 0;
else
return -1;
}
usort($array, 'custom_sale_sort');
If you need your array sorted in the other direction, then switch the (1,-1) values around in the custom function.
Here is the class you can use to do multidimension sort
Note: You must have PHP5
class MultiDimensionSort
{
const ASCENDING = 0,DESCENDING = 1;
public $sortColumn,$sortType;
public function __construct($column = 'price', $type = self::ASCENDING)
{
$this->column = $column;
$this->type = $type;
}
public function cmp($a, $b)
{
switch($this->type)
{
case self::ASCENDING:
return ($a[$this->column] == $b[$this->column]) ? 0 : (($a[$this->column] < $b[$this->column]) ? -1 : 1);
case self::DESCENDING:
return ($a[$this->column] == $b[$this->column]) ? 0 :(($a[$this->column] < $b[$this->column]) ? 1 : -1);
default:
assert(0); // unkown type
}
}
}
Like you have array named summary with contain above array. than you can do sort by following statements.
// assuming your array variable is $summary
$s = new MultiDimensionSort('total_sales', MultiDimensionSort::DESCENDING); // sort by total_sales
usort($summary, array($s, 'cmp'));
print"<pre>";print_r($summary);
Cheers!
May be this ll help you

Sorting array according to the values in PHP

I have the following array
[0] => Array
(
[id] => 229
[val] => 2
)
[3] => Array
(
[id] => 237
[val] => 1
)
[4] => Array
(
[id] => 238
[val] => 6
)
I need to sort this array according to the val values in the array, and do not know how to accomplish this?
function cmp($a, $b)
{
if ($a["val"] == $b["val"]) {
return 0;
}
return ($a["val"] < $b["val"]) ? -1 : 1;
}
usort($yourarray, "cmp");
Read this for more information.
array_multisort can help with this, example 3 presents a similar problem and solution.
This would help - http://www.informit.com/articles/article.aspx?p=341245&seqNum=7
You can use array_multisort()
Examples here: http://www.php.net/manual/en/function.array-multisort.php
The Example #3 Sorting database results is what you want. Might be easier if you are not familiar with callback functions and usort().
use this function to sort array accroding to your need
function sksort(&$array, $subkey="id",$sort_ascending=false)
{
if (count($array))
$temp_array[key($array)] = array_shift($array);
foreach($array as $key => $val){
$offset = 0;
$found = false;
foreach($temp_array as $tmp_key => $tmp_val)
{
if(!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey]))
{
$temp_array = array_merge(
(array)array_slice($temp_array,0,$offset),
array($key => $val),
array_slice($temp_array,$offset)
);
$found = true;
}
$offset++;
}
if(!$found) $temp_array = array_merge($temp_array, array($key => $val));
}
if ($sort_ascending) $array = array_reverse($temp_array);
else $array = $temp_array;
}
==========================================================================
now use this function in ur array
sksort($arrayname, "val"); /* for ascending */
sksort($arrayname, "val", true); /* for descending */

Categories