I have an array as follows:
//0-based index, 2 dimensions
$data = array(
array(
'name'=>'EJA210E',
'id'=>439,
'region_id'=>17,
.. other attributes ..
),
array(
'name'=>'EJA210E',
'id'=>440,
'region_id'=>3,
),
array(
'name'=>'EJA210E',
'id'=>439,
'region_id'=>15,
),
.. etc..
);
What would be sort, first by name, then by id, then by region_id? Sorting by any one of these is no problem; I would simply loop through and get the name attribute, then re-order, however doing this three times I do not understand how to do.
You can try array_multisort by passing different ordering columns as shown below :
$data = array(
array(
'name'=>'EJA210E',
'id'=>439,
'region_id'=>17,
),
array(
'name'=>'EJA210E',
'id'=>440,
'region_id'=>3,
),
array(
'name'=>'AJA210E',
'id'=>438,
'region_id'=>15,
)
);
// Obtain a list of columns
foreach ($data as $key => $row) {
$name[$key] = $row['name'];
$id[$key] = $row['id'];
$region[$key] = $row['region_id'];
}
// Sort the data with volume descending, edition ascending
// Add $data as the last parameter, to sort by the common key
array_multisort($name, SORT_DESC, $id, SORT_DESC,$region, SORT_DESC, $data);
You could use usort() with a custom sorting function. Here's an example:
function cmp($a, $b)
{
if ($a['name'] == $b['name']) {
if ($a['id'] == $b['id']) {
if ($a['region_id'] == $b['region_id']) {
return 0;
}
return $a['region_id'] < $b['region_id'] ? -1 : 1;
}
return $a['id'] < $b['id'] ? -1 : 1;
}
return ($a['name'] < $b['name']) ? -1 : 1;
}
usort($data, 'cmp');
Related
When I am adding same unit of number in "num". eg(0-9). It is sorting my array. But if any of the "num" value contains a different unit of digits, the sorting fails. eg(4,7,2,1) this will work. (7,12,76,2) this will fail.
$stack = array(array("Price" => $op,"num" => $noi),
array("Price" => $op1,"num" => $noi1),
array("Price" => $op2,"num" => $noi2),
array("Price" => $op3,"num" => $noi3));
function cmp($a, $b)
{
return strcmp($a["num"], $b["num"]);
}
usort($stack, "cmp");
Try this for comparison of numbers
function cmp($a, $b) {
if ($a['num'] == $b['num']) {
return 0;
}
return ($a['num'] < $b['num']) ? -1 : 1;
}
It is taken straight out of the PHP manual on usort, and modified for your array.
I found this code for sorting:
usort($array, function($a, $b) {
return $a['order_inside_level'] - $b['order_inside_level'];
});
It's good for one level. I have something like that:
array(
array(
'level'=>'aaa',
'order'='1',
'subs'=>array(
array(
'level'=>'bbb',
'order'='1',
'subs'=>array(
array(
'level'=>'ccc',
'order'='1',
'subs'=>array(
array(
'level'=>'ddd',
'order'='1',
'subs'=>array(
...
)
)
)
)
,
array(
'level'=>'ccc',
'order'='2',
'subs'=>array(
)
)
),
array(
'level'=>'bbb',
'order'='2'
),
array(
'level'=>'bbb',
'order'='3'
)
)
)
),
array(
'level'=>'aaa',
'order'='2',
'subs'=>array(
)
)
)
Array may have any depth and any number of elements in each level. I need to sort each level of depth (aaa,bbb,ccc, etc) using the code above by key 'order'.
You will need to do this recursively.
recursive_sort($arr, $func) {
foreach ($arr as $key => $val) {
if (is_array($val)) {
recursive_sort($val, $func);
}
}
usort($arr, $func);
}
This code will iterate over the given array, and for each value that is an array, call itself with the value. The result is that usort will be called for every array within the structure.
You would call the function the same as you would usort:
recursive_sort($array, function($a, $b) {
return $a['order_inside_level'] - $b['order_inside_level'];
});
function recursive_sort(&$arr) {
fs($arr);
foreach($arr as $k=> &$v){
if (isset($v['subs'])) {
recursive_sort($v['subs']);
}
}
}
function sortByOrder($a, $b) {
return $a['order_inside_level'] - $b['order_inside_level'];
}
function fs(&$array){
usort($array, 'sortByOrder');
}
After multiple tries I have this. It works. Name of key (subs) is hardcoded what is not so good, but... I am glad it works.
I am trying create an array in a foreach loop, and then sort it by a key.
The for each loop creating the array looks like this:
public function index(){
$query=$this->My_model->get_data();
foreach ($query as $row)
{
$data=array(
Array('Points'=>$points,'Name'=>$row['Name'], 'Phone'=>$row['phone']),
);
function cmp ($a, $b) {
return $a['Points'] < $b['Points'] ? 1 : -1;
}
usort($data, "cmp");
print_r($data);
}
}
But this only returns the first the first item in the array.
However when I input some array items directly such as the below, it works fine and sorts all the array items.
public function index(){
$query=$this->My_model->get_data();
foreach ($query as $row)
{
$data = array (
Array ( 'Points' => 500, 'Name' => 'James Lion' ) ,
Array ( 'Points' => 1200, 'Name' => 'John Smith' ),
Array ( 'Points' => 700, 'Name' => 'Jason Smithsonian' ) );
function cmp ($a, $b) {
return $a['Points'] < $b['Points'] ? 1 : -1;
}
usort($data, "cmp");
print_r($data);
}
}
How do I fix this so that the code in the first snippet, so that works as it does in the second snippet?
You have to change the code piece like this
$data[]=array('Points'=>$points,'Name'=>$row['Name'], 'Phone'=>$row['phone']));
The problem with your code is , you are not creating a multidimensional array and instead overwriting the $row values in $data which eventually has the last data since all the other data is overwritten
Also move your function cmp outside of the foreach loop
Have you tried using your custom sort on the outside, (after building your array on the loop). Consider this example:
public function index()
{
$query = $this->My_model->get_data();
foreach ($query as $row) {
$data[] = array('Points' => $points,' Name' => $row['Name'], 'Phone' => $row['phone']),);
}
function cmp ($a, $b) {
return $a['Points'] < $b['Points'] ? 1 : -1;
}
usort($data, "cmp");
print_r($data);
}
Store your values into array format
lock like this $data[]
foreach ($query as $row)
{
$data[]=array(
Array('Points'=>$points,'Name'=>$row['Name'], 'Phone'=>$row['phone']),
);
}
Then you print the data outside of foreach loop
print_r($data);
How can i sort something like this
$a = array(
0 => array(
'field' => array(
'id'=>'valueid'
)
)
);
by valueid? I have managed to sort it by field but i can't realize how to do this weird sorting
usort($a, function($e1, $e2) {
return $e1['field']['id'] > $e2['field']['id'];
});
Use uasort()
uasort($a, function($x, $y) {
return $x['field']['id'] > $y['field']['id'];
});
So I have an array of items in php, some may be linked to others via a parent_id key. I'm looking to sort this array so that any items whose parent is in this array ends up positioned right below the parent.
example: (actual array has many more keys)
some_array[0]['id'] = 15001;
some_array[0]['parent_id'] = 14899;
some_array[1]['id'] = 14723;
some_array[1]['parent_id'] = 0; //parent_id of 0 means item has no parent of its own
some_array[2]['id'] = 14899;
some_array[2]['parent_id'] = 0;
some_array[3]['id'] = 15000;
some_array[3][parent_id'] = 14723;
I'd like to sort these so they end up in this order:
some_array[0]['id'] = 14723;
some_array[1]['id'] = 15000;
some_array[2]['id'] = 14899;
some_array[3]['id'] = 15001;
ie. items are just below their parents.
Thanks in advance!
My shorter version of mattwang's answer:
/**
* sort parents before children
*
* #param array $objects input objects with attributes 'id' and 'parent'
* #param array $result (optional, reference) internal
* #param integer $parent (optional) internal
* #param integer $depth (optional) internal
* #return array output
*/
function parent_sort(array $objects, array &$result=array(), $parent=0, $depth=0) {
foreach ($objects as $key => $object) {
if ($object->parent == $parent) {
$object->depth = $depth;
array_push($result, $object);
unset($objects[$key]);
parent_sort($objects, $result, $object->id, $depth + 1);
}
}
return $result;
}
Only actual difference is that it sorts an array of objects instead of an array of arrays.
I doubt that you guys are still looking for a real answer to this, but it might help out others with the same problem. Below is a recursive function to resort an array placing children beneath parents.
$initial = array(
array(
'name' => 'People',
'ID' => 2,
'parent' => 0
),
array(
'name' => 'Paul',
'ID' => 4,
'parent' => 2
),
array(
'name' => 'Liz',
'ID' => 5,
'parent' => 2
),
array(
'name' => 'Comus',
'ID' => 6,
'parent' => 3
),
array(
'name' => 'Mai',
'ID' => 7,
'parent' => 2
),
array(
'name' => 'Titus',
'ID' => 8,
'parent' => 3
),
array(
'name' => 'Adult',
'ID' => 9,
'parent' => 6
),
array(
'name' => 'Puppy',
'ID' => 10,
'parent' => 8
),
array(
'name' => 'Programmers',
'ID' => 11,
'parent' => 4
) ,
array(
'name' => 'Animals',
'ID' => 3,
'parent' => 0
)
);
/*---------------------------------
function parentChildSort_r
$idField = The item's ID identifier (required)
$parentField = The item's parent identifier (required)
$els = The array (required)
$parentID = The parent ID for which to sort (internal)
$result = The result set (internal)
$depth = The depth (internal)
----------------------------------*/
function parentChildSort_r($idField, $parentField, $els, $parentID = 0, &$result = array(), &$depth = 0){
foreach ($els as $key => $value):
if ($value[$parentField] == $parentID){
$value['depth'] = $depth;
array_push($result, $value);
unset($els[$key]);
$oldParent = $parentID;
$parentID = $value[$idField];
$depth++;
parentChildSort_r($idField,$parentField, $els, $parentID, $result, $depth);
$parentID = $oldParent;
$depth--;
}
endforeach;
return $result;
}
$result = parentChildSort_r('ID','parent',$initial);
print '<pre>';
print_r($result);
print '</pre>';
It's a wind down method that removes elements from the original array and places them into result set in the proper order. I made it somewhat generic for you, so it just needs you to tell it what your 'ID' field and 'parent' fields are called. Top level items are required to have a parent_id (however you name it) of 0.
You can use usort to sort by a user defined function:
function cmp($a, $b)
{
if ( $a['id'] == $b['id'] ) {
return 0;
} else if ( $a['parent_id'] ) {
if ( $a['parent_id'] == $b['parent_id'] ) {
return ( $a['id'] < $b['id'] ? -1 : 1 );
} else {
return ( $a['parent_id'] >= $b['id'] ? 1 : -1 );
}
} else if ( $b['parent_id'] ) {
return ( $b['parent_id'] >= $a['id'] ? -1 : 1);
} else {
return ( $a['id'] < $b['id'] ? -1 : 1 );
}
}
usort($some_array, "cmp");
Note: this will only work with a tree that is one level deep (meaning no children of children). For more complex trees you probably want to sort the data into a graph and then flatten it.
Edit: fixed to edit a case where $b has a parent but $a does not.
Just use usort() function and compare two different elements of the 'big array' in a way you need. This becomes then a question about 'how do I really decide which element is before which element?'.
The simple usort won't work if you want to support more than one layer of children. There's simply no way to know how two arbitrary elements compare without other information.
I didn't think about it much, so perhaps it doesn't work. But here's a sorting class:
class TopSort
{
private $sorted, $unsorted;
private $history;
public function sort(array $unsorted)
{
$this->sorted = array();
$this->unsorted = $unsorted;
$this->history = array();
usort($this->unsorted, function($a, $b)
{
return $b['id'] - $a['id'];
});
foreach ($this->unsorted as $i => $a)
if ($a['parent_id'] == 0) $this->visit($i);
return array_reverse($this->sorted);
}
private function visit($i)
{
if (!array_key_exists($i, $this->history))
{
$this->history[$i] = true;
foreach ($this->unsorted as $j => $a)
if ($a['parent_id'] == $this->unsorted[$i]['id']) $this->visit($j);
$this->sorted[] = $this->unsorted[$i];
}
}
}
$sorter = new TopSort();
$some_array = $sorter->sort($some_array);
The idea here is to first sort in reverse by id. Then build up a new array by inserting the deepest elements (those with no children) first. Since we initially sorted the array by reverse id, it should mean the entire thing is upside down. After reversing the array, it should be exactly like you want. (Of course one could unshift items onto the array to avoid the reverse operation, but that might be slower...)
And this is very unoptimized as it iterates over the entire array many, many times. With a little rework, it wouldn't need to do that.
Here's an alternative class that is more optimized:
class TopSort
{
private $sorted;
public function sort(array $nodes)
{
$this->sorted = array();
# sort by id
usort($nodes, function($a, $b) {
return $a['id'] - $b['id'];
});
# build tree
$p = array(0 => array());
foreach($nodes as $n)
{
$pid = $n['parent_id'];
$id = $n['id'];
if (!isset($p[$pid]))
$p[$pid] = array('child' => array());
if (isset($p[$id]))
$child = &$p[$id]['child'];
else
$child = array();
$p[$id] = $n;
$p[$id]['child'] = &$child;
unset($child);
$p[$pid]['child'][] = &$p[$id];
}
$nodes = $p['0']['child'];
unset($p);
# flatten array
foreach ($nodes as $node)
$this->flatten($node);
return $this->sorted;
}
private function flatten(array $node)
{
$children = $node['child'];
unset($node['child']);
$this->sorted[] = $node;
foreach ($children as $node)
$this->flatten($node);
}
}
$sorter = new TopSort();
$sorted = $sorter->sort($some_array);
It's a three step approach:
Sort by id (usort)
Build nested array structure.
Flatten array in pre-order.
By virtue of presorting by id, each group of children should be sorted correctly.