I want to sort this array by year:
Array
(
[0] => data/pictures/alice/1980
[1] => data/pictures/alice/1985
[2] => data/pictures/bob/1981
[3] => data/pictures/bob/1985
[4] => data/pictures/bob/1987
[5] => data/pictures/bob/1989
)
Expected result:
Array
(
[0] => data/pictures/alice/1980
[1] => data/pictures/bob/1981
[2] => data/pictures/alice/1985
[3] => data/pictures/bob/1985
[4] => data/pictures/bob/1987
[5] => data/pictures/bob/1989
)
I've already tried different sort functions without success.
Example:
asort($paths, SORT_STRING | SORT_FLAG_CASE);
sort($path, SORT_NUMERIC);
Since it's a path just map the array through basename() and then sort based on that:
array_multisort(array_map('basename', $paths), SORT_ASC, $paths);
Try this
function cmp($a, $b) {
// if equal, don't do much
if ($a == $b) {
return 0;
}
$explodedA = explode('/', $a);
$explodedB = explode('/', $b);
$yearPartA = $explodedA[count($explodedA) - 1];
$yearPartB = $explodedB[count($explodedB) - 1];
if ($explodedPartA == $explodedPartB) { // compare full string
return ($a < $b) ? -1 : 1;
}
return ($yearPartA < $yearPartB) ? -1 : 1;
}
// actual sort of the array $path (e.g. the whole point)
usort($path, "cmp");
Consider, however that you'd probably be doing 'explode' several times for each array element and that it might be cheaper to work a bit on the array first. Not sure how big your array is... Do some testing.
$array = ['data/pictures/alice/1980','data/pictures/alice/1985','data/pictures/bob/1981','data/pictures/bob/1985','data/pictures/bob/1987','data/pictures/bob/1989'];
uasort($array, function($a,$b) {
$y1 = array_pop(explode('/', $a));
$y2 = array_pop(explode('/', $b));
if($y1===$y2) {
// if year the same use other criteria
if($a===$b) {
return 0;
}
return $a>$b?-1:1;
};
return $y1>$y2?-1:1;
});
Use usort and in the custom function explode the strings by "/" and compare the last parts of the arrays.
Related
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.
Hello how to sort arrays by keys and values too... so if user input this value
$input = array(0,1,0,2,0);
then the result should be like this since they're the same input they should maintain their keys too...
Array
(
[0] => 0
[2] => 0
[4] => 0
[1] => 1
[3] => 2
)
not like this... the keys is jumbled and I really that key to work on my project of FCFS Scheduling.
Array
(
[4] => 0
[0] => 0
[2] => 0
[1] => 1
[3] => 2
)
btw I used asort. someone help me how to fix this?
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(0,1,0,2,0);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo " $value\n";
}
Stable sort would help here. But php don't have any stable sorting functions since 4.1.
But you can use uksort + closure.
$input = array(0,1,0,2,0);
$cmp = function($a, $b) use($input){
if($input[$a] > $input[$b]){return 1;}
elseif($input[$a] < $input[$b]){return -1;}
elseif($a>$b){return 1;}
elseif($a<$b){return -1;}
return 0;
};
uksort($input, $cmp);
print_r($input);
https://eval.in/145923
Or shorter version
$cmp = function($a, $b) use($input){
return (($input[$a]-$input[$b])?:($a-$b));
};
Simple use the sort function
$input = array(0,1,0,2,0);
sort($input);
Result:-
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 2
)
So I currently have this:
array(0=>'foo', 1=>'bar', 3=>'baz', 4=>'boo', 5=>'wahoo');
What I want is this:
array(0=>'foo', 3=>'baz', 1=>'bar', 5=>'wahoo', 4=>'boo');
This is a simplified example, my actual array is much larger and more complex so it can't easily be broken into smaller pieces and re-assembled.
I've been using uksort to attempt this, which I think is the best way forward but can't seem to get the results I want.
Edit:
I think my simplified example is actually confusing the issue. Here is my actual array and what I want to end up with.
Array
(
[1820] => Safety
[1821] => Security
[1822] => Digital Life
[1893] => Privacy and Digital Footprints
[1823] => Connected Culture
[1824] => Respecting Creative Work
[1825] => Searching
[1826] => Research and Evaluation
[1836] => Self-Expression and Identity
)
Array
(
[1820] => Safety
[1821] => Security
[1822] => Digital Life
[1893] => Privacy and Digital Footprints
[1823] => Connected Culture
[1836] => Self-Expression and Identity
[1824] => Respecting Creative Work
[1825] => Searching
[1826] => Research and Evaluation
)
So I almost have a numeric sort with two items pulled out of sequence.
You're actually looking for uasort.
That will let you use your own sorting function and preserve the keys.
uksort sorts based on the keys, not the values.
Example:
http://codepad.viper-7.com/E8oZ2g
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
// Array to be sorted
$array = array('a', 'b', 'c', 'b');
echo "<pre>";
print_r($array);
echo "</pre>";
// Sort and print the resulting array
uasort($array, 'cmp');
echo "<pre>";
print_r($array);
echo "</pre>";
Output:
Array
(
[0] => a
[1] => b
[2] => c
[3] => b
)
Array
(
[0] => a
[3] => b
[1] => b
[2] => c
)
New answer based on discussion.
Do use uksort()
Do a switch in the compare function for the ones that match and change them.
Example that works for your example above.
function updateKey($key)
{
switch($key)
{
case 1893:
return 1822.5;
case 1836:
return 1823.5;
default:
return $key;
}
}
function cmp($a, $b)
{
$a = updateKey($a);
$b = updateKey($b);
// you must do the compare this way for floats (instead of just subtracting) because php implemented the compare callback poorly
if ($a == $b)
{
return 0;
}
return ($a < $b) ? -1 : 1;
}
uksort($array, 'cmp');
//randomize array preserving keys
function shuffle_assoc( $array )
{
$keys = array_keys( $array );
shuffle( $keys );
return array_merge( array_flip( $keys ) , $array );
}
Source: http://www.php.net/manual/en/function.shuffle.php
Note that this returns a new array, it doesn't modify the input array.
Let's say you have two arrays of arrays with the same structure but different count of arrays in them:
$arr1 = array(array(1,"b"), array(2,"a"), array(5,"c"));
$arr2 = array(array(3,"e"));
Now, the data in the $arr1 and $arr2 is sorted, and now what I would like it to merge these two arrays, so I did this:
$res = array_merge($arr1, $arr2);
And then I get an output like this:
1-b
2-a
5-c
3-e
But, I would like to have a sorted $res also like this:
1-b
2-a
3-e
5-c
I wonder if there's a function in PHP to do this automatically, without me having to write my own function? Or, please advise me on which is the best approach for this if I want to (later on) add sorting by the next parameter so the output would be like this
2-a
1-b
5-c
3-e
Thank you for all your help.
You can first merge the arrays and then sort the final array.
You are probably looking for a multi-sort function. I usually use this function (I found this functions somewhere on the internet years ago, credits go to the original author):
/*
* sort a multi demensional array on a column
*
* #param array $array array with hash array
* #param mixed $column key that you want to sort on
* #param enum $order asc or desc
*/
function array_qsort2 (&$array, $column=0, $order="ASC") {
$oper = ($order == "ASC")?">":"<";
if(!is_array($array)) return;
usort($array, create_function('$a,$b',"return (\$a['$column'] $oper \$b['$column']);"));
reset($array);
}
You can use it like this:
array_qsort2($res, 0, "ASC");
Why not simply call ksort($res) after your array_merge?
Since php v5.3 you can use anon functions in a more natural manner,
<?php
$arr1 = array(array(1,"b"), array(2,"a"), array(5,"c"));
$arr2 = array(array(3,"e"));
$res = array_merge($arr1, $arr2);
usort($res, function($a,$b) {
// php7
// return $a[0] <=> $b[0];
if ($a[0] == $b[0]) return 0;
return $a[0] < $b[0] ? -1 : 1;
});
print_r($res);
output
Array
(
[0] => Array
(
[0] => 1
[1] => b
)
[1] => Array
(
[0] => 2
[1] => a
)
[2] => Array
(
[0] => 3
[1] => e
)
[3] => Array
(
[0] => 5
[1] => c
)
)
You can use below function to merge two sorted arrays without array_merge() or sort().
<?php
$arr1 = [1,2,5,7,10,20,36,70,82,90];
$arr2 = [4,6,10,15,65,85,90,100];
function shortt($array1,$array2){
$array1_count = count($array1);
$array2_count = count($array2);
while ($array1_count || $array2_count)
{
$array1_empty = $array1 == [];
$array2_empty = $array2 == [];
if (!$array1_empty && !$array2_empty)
{
if (current($array1) < current($array2))
{
$array3[] = array_shift($array1);
$array1_count--;
}
else
{
$array3[] = array_shift($array2);
$array2_count--;
}
}
elseif (!$array1_empty)
{
$array3[] = array_shift($array1);
$array1_count--;
}
elseif (!$array2_empty)
{
$array3[] = array_shift($array2);
$array2_count--;
}
}
return $array3;
}
$newarr = shortt($arr1,$arr2);
print_r($newarr);
?>
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 */