I have the following array:
array(10) {
[0]=> array(109) {
["id"]=> string(4) "2632", ["_category_sortorder"] => 8, ["_sortorder" => 1]
},
[1]=> array(109) {
["id"]=> string(4) "2635", ["_category_sortorder"] => 5, ["_sortorder" => 2]
},
...
}
I want to sort it based on two criterias:
a) by _category_sortorder asc (with priority)
b) by _sortorder asc
I tried this:
foreach($resources as $k => $v) {
$sort[$k]['_category_sortorder'] = $resources[$k]['_category_sortorder'];
$sort[$k]['_sortorder'] = $resources[$k]['_sortorder'];
}
array_multisort($sort['_category_sortorder'], SORT_ASC, $sort['_sortorder'], SORT_ASC, $resources);
But it's not working as expected. Any suggestions?
Try like this,
$sort = array(
array("id"=>"263", "_category_sortorder"=> 8, "_sortorder" => 1),
array( "id"=> "145", "_category_sortorder" => 155, "_sortorder" => 2),
array( "id"=> "2145", "_category_sortorder" => 55, "_sortorder" => 12),
array( "id"=> "3145", "_category_sortorder" => 155, "_sortorder" => 10),
);
usort($sort, function(array $a, array $b) {
return $b['_category_sortorder'] - $a['_category_sortorder'];
});
echo '<pre>';
print_r($sort);
echo '</pre>';
I think there is a simple solution but this code block is doing is job:
You split the array in sub arrays including only elements with the same _category_sortorder value. Then you sort each sub array with usort. At the end you merge them together.
<?php
$array = [
['id' => 1, '_category_sortorder' => 2, '_sortorder' => 1],
['id' => 2, '_category_sortorder' => 2, '_sortorder' => 3],
['id' => 3, '_category_sortorder' => 3, '_sortorder' => 19],
['id' => 4, '_category_sortorder' => 1, '_sortorder' => 2],
['id' => 5, '_category_sortorder' => 1, '_sortorder' => 1],
];
foreach ($array as $value) {
if (!isset($newElements[$value['_category_sortorder']]))
$newElements[$value['_category_sortorder']] = [];
$newElements[$value['_category_sortorder']][] = $value;
}
$array = [];
foreach ($newElements as $key => $value) {
usort($value, function($a, $b) {
return strcmp($a["_sortorder"], $b["_sortorder"]);
});
$array[$key] = $value;
}
ksort($array);
$new = [];
foreach ($array as $value) {
$new = array_merge($new, $value);
}
echo "<pre>".print_r($new, true).'</pre>';
Related
Given the following multidimensional array:
$menu = [
'root' => [
'items' => [
'A' => [
'p' => [1, 2, 9],
],
'B' => [
'p' => [1, 2, 3, 9],
],
'C' => [
'p' => [1, 2, 4, 9],
],
'D' => [
'items' => [
'D1' => [
'p' => [1, 2, 3, 4, 9],
],
'D2' => [
'p' => [1, 2, 3, 4],
],
],
],
'E' => [
'items' => [
'E1' => [
'p' => [1, 2, 10],
],
],
],
'F' => [
'items' => [
'F1' => [
'p' => [5, 6],
],
'F2' => [
'p' => [7, 8],
],
],
],
],
],
];
Is there a way to get all the values in the 'p's as array, uniquely?
The output should be [1, 2, 9, 3, 4, 10, 5, 6, 7, 8]
I tried a simple one-liner, but it works only for the first level (A, B, C), nested $items are ignored:
$ps = array_unique(call_user_func_array('array_merge', array_column($menu['root']['items'], 'p')));
print_r($ps);
I also tried to write a recursive function, but I get totally stuck and the output is not what's expected
function recursive_ps($elem, $arr = []){
$output = $arr;
if (isset($elem['items'])){
foreach($elem['items'] as $key => $value){
if (isset($value['p'])){
$output = array_merge($arr, $value['p']);
if (isset($value['items'])){
return recursive_ps($value, $output);
}
}
}
}
return $output;
}
$o = recursive_ps($menu['root']);
print_r($o);
Please, any help?
The main issue is to return recursive_ps in the loop. You have to merge the returned data with the current array.
The second if to test $values['items'] shouldn't be inside the $value[p].
Also, $output = array_merge($arr, $value['p']); should be $output = array_merge($output, $value['p']); to combine to the current array.
Working code: (demo)
function recursive_ps($elem)
{
if (!isset($elem['items'])) {
return [];
}
$output = [];
foreach($elem['items'] as $key => $value) {
if (isset($value['p'])) {
$output = array_merge($output, $value['p']);
}
if (isset($value['items'])) {
// merge the return value of function
$output = array_merge($output, recursive_ps($value));
}
}
return $output;
}
$output = array_values(array_unique(recursive_ps($menu['root'])));
print_r($output);
Output:
Array
(
[0] => 1
[1] => 2
[2] => 9
[3] => 3
[4] => 4
[5] => 10
[6] => 5
[7] => 6
[8] => 7
[9] => 8
)
Another version using reference :
function recursive_ps($elem, &$arr = []) {
if (isset($elem['items'])) {
foreach($elem['items'] as $key => $value) {
if (isset($value['p'])) {
$arr = array_merge($arr, $value['p']);
}
if (isset($value['items'])) {
recursive_ps($value, $arr); // pass array by reference
}
}
}
}
$output = [];
recursive_ps($menu['root'], $output); // pass array by reference
print_r(array_values(array_unique($output)));
I'd like to re-index a multidimensional array to be like this:
[
0 => ['id' => 1, 'children' => [['id' => 2], ['id' => 3]]],
4 => ['id' => 5, 'children' => [['id' => 6], ['id' => 7]]]
8 => ...
]
If I use array_walk_recursive I get:
[
0 => ['id' => 1, 'children' => [['id' => 2], ['id' => 3]]],
1 => ['id' => 4, 'children' => [['id' => 5], ['id' => 6]]]
2 => ...
]
This is almost there, but not quite...
array_walk_recursive($out, function(&$item, $key) {
if($key == 'id')
{
$item = $this->_i;
$this->_i++;
}
});
Managed to solve this, it not pretty (I later found that the problem was elsewhere and left the code in an proof of concept state). Posting this if anyone else need this kind of structure.
// restructure the array for reindexing
$out = [];
$k = 0;
foreach ($arr as $as)
{
$test = ['id' => $k, 'items' => $as];
array_push($out, $test);
$k++;
}
// reindex multidimensiona array
$i = 1;
array_walk_recursive($out, function(&$item, $key) {
global $i;
if($key == 'id')
{
$item = $i;
$i++;
}
});
// final array restructuring
$res = [];
foreach($out as $oo)
{
$firstKey = array_key_first($oo);
$res[$oo[$firstKey]] = $oo['items'];
}
var_dump( $res );
Lets assume I have a soccer league array:
$array = [
'teamId1' =>
[
'games' => 7,
'wins' => 2,
'loss' => 4,
'duals' => 1,
'point' => 7
],
'teamId2' =>
[
'games' => 7,
'wins' => 3,
'loss' => 1,
'duals' => 3,
'point' => 12
],
'teamId3' =>
[
'games' => 7,
'wins' => 4,
'loss' => 3,
'duals' => 0,
'point' => 12
],
'teamId4' =>
[
'games' => 7,
'wins' => 6,
'loss' => 0,
'duals' => 1,
'point' => 19
],
'teamId5' =>
[
'games' => 7,
'wins' => 3,
'loss' => 2,
'duals' => 2,
'point' => 11
],
.
.
.
'teamId18' =>
[
'games' => 7,
'wins' => 5,
'loss' => 0,
'duals' => 2,
'point' => 17
],
];
and I want to sort this By POINT DESC; so I did use this function to sort my array by point:
function sortArray($array, $on, $order=SORT_ASC)
{
$new_array = array();
$sortable_array = array();
if (count($array) > 0)
{
foreach ($array as $k => $v)
{
if (is_array($v))
{
foreach ($v as $k2 => $v2)
{
if ($k2 == $on)
{
$sortable_array[$k] = $v2;
}
}
}
else
{
$sortable_array[$k] = $v;
}
}
switch ($order) {
case SORT_ASC:
asort($sortable_array);
break;
case SORT_DESC:
arsort($sortable_array);
break;
}
foreach ($sortable_array as $k => $v)
{
$new_array[$k] = $array[$k];
}
}
return $new_array;
}
$sortByPoint = sortArray($result, 'point', SORT_DESC);
Ok, now I have an array sorted by point, but now I need something like fine tune my array to reorder array in which when two-or more- teams have same point, the team/s with more wins come before other.
I know I can loop through sorted array and do something, but I'm sure there's shorter and tricky way to do that.
Does anyone any idea about that?
Thanks in Advance
It would be easier to write a specific sort for this. Using usort(), you can add layers of complexity to the conditions as you need it in a simple function...
usort($array, function ($a, $b) {
if ( $a['point'] == $b['point'] ){
// Sort by wins (descending)
return $b['wins'] - $a['wins'];
}
// Sort by points descending
return $b['point'] - $a['point'] ;
});
print_r($array);
This sorts the actual array, so output $array.
You could expand it further by saying if the same number of wins, sort by losses etc.
I have these 2 arrays.
First array is from user input $cart:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80],
['id' => 1, 'weight' => 50, 'percentage' => 80],
['id' => 2, 'weight' => 40, 'percentage' => 80],
];
and second array, I do a database SELECT id, stock WHERE id IN (3,1,2), resulting $db_item
$db_item = [
['id' => 1, 'stock' => 9539.00],
['id' => 2, 'stock' => 9468.00],
['id' => 3, 'stock' => 9295.00],
];
I want to add the stock attribute in second array to first array.
Expected output:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80, 'stock' => 9295.00],
['id' => 1, 'weight' => 50, 'percentage' => 80, 'stock' => 9539.00],
['id' => 2, 'weight' => 40, 'percentage' => 80, 'stock' => 9468.00],
];
This is what I tried, and it works, but I don't think it is necessary to have foreach, array_filter, and array_column:
foreach ($cart as $key => $cart_item) {
$item = array_filter($db_item, function($item) use ($cart_item) {
return $item['id'] === $cart_item['id'];
});
$cart[$key]['stock'] = array_column($item, 'stock')[0];
}
anyone has better idea how to optimize this?
EDIT: following Mohammad's answer, I can use more attribute in second array
$keys = [];
foreach ($arr2 as $item) {
$keys[$item['id']] = array(
'attr1' => $item['attr1'],
'attr2' => $item['attr2'],
// and so on
);
}
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
EDIT2: found out that we can simplify the foreach loop with just a single line using array_column.
$keys = array_column($arr2, null, 'id');
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
Use combination of array_flip() and array_column() to create array contain id and index of second array.
Then use array_map() to add new key stock to first array.
$keys = array_flip(array_column($arr2, 'id'));
$newArr = array_map(function($item) use($keys, $arr2){
$item['stock'] = $arr2[$keys[$item['id']]]['stock'];
return $item;
}, $arr1);
Check result in demo
Also you can use foreach instead of array_flip()
$keys = [];
foreach ($arr2 as $item)
$keys[$item['id']] = $item['stock'];
$newArr = array_map(function($item) use($keys){
$item['stock'] = $keys[$item['id']];
return $item;
}, $arr1);
Check result in demo
This can help too, one array_column + one array_map :
$arr2=array_column($arr2,'stock','id');
$arr1=array_map(function($val)use($arr2){$val['stock']=$arr2[$val['id']];return $val;},$arr1);
Is there built-in function, or shorter way to extract elements into new array, as described here?
<?php
function arr_slice ($arr, $keys) {
$ret = array();
foreach ($keys as $k) { $ret[$k] = $arr[$k]; }
return $ret;
}
$array = array(
"a" => 1,
"b" => 2,
"c" => 3,
"d" => 4,
);
var_export(
arr_slice($array, array("x","d","b"))
);
output (key order matters)
array (
'x' => NULL,
'd' => 4,
'b' => 2,
)