Multidimensional array sort on multiple keys - php
Hi I've been using the array_sort() function found here for some time to sort results from multiple APIs but now I have the need to sort by two keys simultaneously.
The two keys I need to sort on are deal_score DESC and date_start DESC
The properties of this array are as follows.
Record 2 has the highest deal_score so should come first
Records 0 and 1 have the same deal_score but date_start is higher on record 1 so the final order of results should be 2, 1, 0
Here's an example array which has been trimmed down for readability.
[0] => Array
(
[db_id] => 414314
[date_start] => 2012-04-17
[deal_score] => 81.3
[deal_statements] => Array
(
[0] => 49.85
[1] => 2.11
)
)
[1] => Array
(
[db_id] => 414409
[date_start] => 2012-04-20
[deal_score] => 81.3
[deal_statements] => Array
(
[0] => 28.2
[1] => 21.41
)
)
[2] => Array
(
[db_id] => 1345923
[date_start] => 2012-04-17
[deal_score] => 85
[deal_statements] => Array
(
[0] => 18.1
[1] => 22.16
)
)
Any help on this will be greatly appreciated.
sth. like this should do:
foreach ($data as $key => $row) {
$score[$key] = $row['deal_score'];
$dates[$key] = $row['date_start'];
}
array_multisort($score, SORT_ASC, $dates, SORT_ASC, $data);
3rd. example on http://php.net/manual/en/function.array-multisort.php pretty much explains it.
Cheers.
Related
Sorting multiple subarrays based on a total count from all sub arrays
I have some data which looks like this (reduced) Array ( [datasets] => Array ( [0] => Array ( [label] => NEW [backgroundColor] => #37fdfd [data] => Array ( [0] => 0 [1] => 0 [2] => 5 [3] => 0 ) ) [1] => Array ( [label] => Grade A [backgroundColor] => #76ef76 [data] => Array ( [0] => 8 [1] => 12 [2] => 11 [3] => 0 ) ) [2] => Array ( [label] => Grade B [backgroundColor] => #f9f96d [data] => Array ( [0] => 1 [1] => 6 [2] => 5 [3] => 3 ) ) [3] => Array ( [label] => Grade C [backgroundColor] => #f3ca36 [data] => Array ( [0] => 3 [1] => 0 [2] => 1 [3] => 4 ) ) [4] => Array ( [label] => Grade D [backgroundColor] => #f3ca36 [data] => Array ( [0] => 3 [1] => 0 [2] => 1 [3] => 0 ) ) ) [labels] => Array ( [0] => User 0 [1] => User 1 [2] => User 2 [3] => User 3 ) ) Here is a JSON string of the data (not reduced, numbers may differ slightly) {"datasets":[{"label":"NEW","backgroundColor":"#37fdfd","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"Grade A","backgroundColor":"#76ef76","data":[9,14,12,0,4,17,13,0,10,0,18,18,12,13,13,4]},{"label":"Grade B","backgroundColor":"#f9f96d","data":[1,6,5,0,6,5,2,0,1,0,2,1,4,3,1,15]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]}],"labels":["User 0","User 1","User 2","User 3","User 4","User 5","User 6","User 7","User 8","User 9","User 10","User 11","User 12","User 13","User 14","User 15"]} Each dataset has an array of data which has keys that directly relates to a key in the labels array. This is currently sorted in alphabetical order by the label. This data structure is the structure required for Chart.js, which I am using to display a stacked bar chart on my webpage. Essentially what I need to accomplish is to sort the data array for every user in the labels array based on the sum of each data set for that user. I also need to sort the labels array to be in the same order. My original idea on how to achieve this is to create a temporary array, loop through all the data sets and add them to this temporary array in the order necessary, but I got stuck after calculating the total for each user. Here is my attempt: $return = []; foreach($calculated['labels'] as $key => &$name) { $total = 0; foreach($calculated['datasets'] as $dataset) { $total += $dataset['data'][$key]; } echo "$name - $total<br>"; } How can I sort my data and labels in descending order based on the total for each user from all datasets. Here is my expected output for the reduced data above Array ( [datasets] => Array ( [0] => Array ( [label] => NEW [backgroundColor] => #37fdfd [data] => Array ( [2] => 5 [1] => 0 [0] => 0 [3] => 0 ) ) [1] => Array ( [label] => Grade A [backgroundColor] => #76ef76 [data] => Array ( [2] => 11 [1] => 12 [0] => 8 [3] => 0 ) ) [2] => Array ( [label] => Grade B [backgroundColor] => #f9f96d [data] => Array ( [2] => 5 [1] => 6 [0] => 1 [3] => 3 ) ) [3] => Array ( [label] => Grade C [backgroundColor] => #f3ca36 [data] => Array ( [2] => 1 [1] => 0 [0] => 3 [3] => 4 ) ) [4] => Array ( [label] => Grade D [backgroundColor] => #f3ca36 [data] => Array ( [2] => 1 [1] => 0 [0] => 3 [3] => 0 ) ) ) [labels] => Array ( [2] => User 2 //23 total across all data sets [1] => User 1 //18 total across all data sets [0] => User 0 //15 total across all data sets [3] => User 3 //7 total across all data sets ) ) The key in the labels array acts as a unique identifier for each user in each dataset data array. Notice how each set of data inside of each dataset is in the same order, as is the labels array. Each set should be ordered by the total amount from all sets for each user, not necessarily the highest number in each dataset. For clarification, each set of data in each dataset contains a list of values, the key for each value is directly related to the key for each user in the labels array. So in my example, we have User 0 who has the key "0". This user has a total of 23 from adding up the values from each dataset with the key "0".
Complete solution: // get array $a = json_decode('{"datasets":[{"label":"NEW","backgroundColor":"#37fdfd","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"label":"Grade A","backgroundColor":"#76ef76","data":[9,14,12,0,4,17,13,0,10,0,18,18,12,13,13,4]},{"label":"Grade B","backgroundColor":"#f9f96d","data":[1,6,5,0,6,5,2,0,1,0,2,1,4,3,1,15]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]},{"label":"Grade C","backgroundColor":"#f3ca36","data":[3,0,1,0,2,0,0,0,0,0,1,1,0,0,0,0]}],"labels":["User 0","User 1","User 2","User 3","User 4","User 5","User 6","User 7","User 8","User 9","User 10","User 11","User 12","User 13","User 14","User 15"]}', true); // get array of arrays with `data` key from each data set $users = array_column($a['datasets'], 'data'); // tricky code to sum arrays $sums = array_map('array_sum', array_map(null, ...$users)); // sort array with keeping keys arsort($sums); // we need flip so as `array_replace` will work as expected $keys = array_flip(array_keys($sums)); // "sorting" `data` subarrays foreach ($a['datasets'] as &$item) { $item['data'] = array_replace($keys, $item['data']); } // "sorting" `labels` subarray $a['labels'] = array_replace($keys, $a['labels']); // see the result print_r($a); Fiddle here https://3v4l.org/a7rPL
I see this task as a perfect candidate for array_multisort(). Your synchronously sorted subarrays don't need to retain their initial keys like in u_mulder's output. The first parameter must be the array of columnar sums, then the descending sort flag as the second parameter, then the labels subarray as a reference, then the dynamic number of data subarrays as references to the original array. Code: (Demo) $params = [[], SORT_DESC, &$array['labels']]; foreach ($array['datasets'] as ['data' => &$data]) { foreach ($data as $i => $d) { $params[0][$i] = ($params[0][$i] ?? 0) + $d; } $params[] = &$data; } array_multisort(...$params); var_export($array);
This looks like a typical job for map(reduce).sort: map each element to an object with id, so you can preserve "which user this used to be" information, and total, the result of reducing data. Then sort with a custom sort function (a,b) => a.total - b.total. E.g. function map_total($user, $pos) { return array( "id" => $pos, "total" => array_sum($user.data) ); } function cmp_total($a, $b) { return $a["total"] - $b["total"]; } $mapped = array_map("map_total", $thing.dataset, array_keys($thing.dataset)); $sorted = usort($mapped, "cmp_total");
PHP reorder multidimensional array based on single dimensional array
multidimensional array Array ( [0] => Array ( [ID] => 5068 [Item] => 2737 [Unit] => 15 ) [1] => Array ( [ID] => 5067 [Item] => 2737 [Unit] => 13 ) ) single dimensional array Array ( [0] => 11 [1] => 13 [2] => 15 ) Expected output: Array ( [0] => Array ( [ID] => 5067 [Item] => 2737 [Unit] => 13 ) [1] => Array ( [ID] => 5068 [Item] => 2737 [Unit] => 15 ) ) It not necessary that single dimensional array must be in ASC/DESC order. I want to reorder multidimensional array according to single dimensional array based on unit value. How can I achieve this?
Maybe something like this would work? $multiDimArray = [ 0 => [ 'ID' => 5068, 'Item' => 2737, 'Unit' => 15 ], 1 => [ 'ID' => 5067, 'Item' => 2737, 'Unit' => 13 ] ]; $singleDim = [ 11,13,15 ]; usort($multiDimArray,function($a,$b){ global $singleDim; return ($singleDim[0] >= $a['Unit']) ? -1 :1; }); print_r($multiDimArray); there's something in back of my head that tells me I'm missing something. But feel free to give it a go.
Assuming that the values in the single array all map to a value in Unit, you could loop the single dimensional array and then loop the values in the multidimensional array. In the inner loop use in_array to check if the value from the single dimensional array occurs in the multidimensional array. If it does, you could for example add it to a new array. $result = []; foreach ($singleDimension as $sm) { foreach ($multiDimensional as $md) { if (in_array($sm, $md)) { $result[] = $md; } } } Demo
How to Extract Values from php array
I'm trying to return the LARGEST numerical value associated with the 'reduced' array below. Easy enough if there is one value but many have two- as below. I'm using $reduced_array = $data['rates'][1]['rates'][0]; but this only works in returning the first value. I need to return just the highest value however- so below it would be 8. would something like if(count($data['rates'][1]['rates']) > 2) { ***return largest value here*** work? I'm just not sure how to perform the asterisked task- maybe a for loop? here is the array. Array ( [rates] => Array ( [0] => Array ( [name] => Super Reduced [rates] => Array ( ) ) [1] => Array ( [name] => Reduced [rates] => Array ( [0] => 5 [1] => 8 ) ) [2] => Array ( [name] => Standard [rates] => Array ( [0] => 23 ) ) [3] => Array ( [name] => Increased [rates] => Array ( ) ) [4] => Array ( [name] => Parking [rates] => Array ( ) ) ) [disclaimer] => Rates data is based on information published by the European Commission, updated 1st January 2017. ) Thanks for any help
You can use max() for that. $array = array( "rates" => array( array( "name" => "Super Reduced", "rates" => array() ), array( "name" => "Reduced", "rates" => array( 5, 8 ) ) ) ); echo max($array["rates"][1]["rates"]); // 8
Easiest solution will be using of max on inner array example: if(!empty($data['rates'][1]['rates'])) { $maxvalue = max($data['rates'][1]['rates']); }
Multidimensional Array - Get unique values, but count all duplicates
I have a multi-dimensional array that I would like to get unique sub-values from, but also have a count of how many times those unique sub-values occurred. For instance, this would be my starting array: [0] => Array ( [0] => Array ( [id] => 1533438473619168 ) [1] => Array ( [id] => 3333333333333333 ) ) [1] => Array ( [0] => Array ( [id] => 1533438473619168 ) [1] => Array ( [id] => 5555555555555555 ) ) [2] => Array ( [0] => Array ( [id] => 1533438473619168 ) [1] => Array ( [id] => 77777777777777777 ) ) In the end, I'd like to have an array that looks like this: [0] => Array ( [0] => Array ( [id] => 1533438473619168 [count] => 3 ) [1] => Array ( [id] => 3333333333333333 [count] => 1 ) [2] => Array ( [id] => 5555555555555555 [count] => 1 ) [3] => Array ( [id] => 77777777777777777 [count] => 1 ) ) Is there any general/easy way to do this without iterating through the first array for each value, comparing/storing the values in a temporary array, checking them, and adding to the count?
To get this exact format you may need to iterate thought your current array and do the counting manually, however php has the array_count_values() and array_unique() functions for this kind of thing: http://php.net/manual/en/function.array-count-values.php http://php.net/manual/en/function.array-unique.php
Because you are only concerned with the deepest values of the array, using array_walk_recursive seems suitable for this. Note that a reference to the output array $counted is used in the callback. array_walk_recursive($ids, function($id, $k) use (&$counted) { $counted[$id] = isset($counted[$id]) ? $counted[$id] + 1 : 1; }); Using the id as the key in the $counted array will simplify the counting. The result of this will be somewhat different from your suggested output, but in my opinion it would actually be simpler to use. (e.g. foreach ($counted as $id => $count) {...). $counted = array( "1533438473619168" => 3 "3333333333333333" => 1 "5555555555555555" => 1 "77777777777777777" => 1);
Sort a multidimensional array in PHP
I have this multidimensional array, called $rent: Array ( [product2] => Array ( [dates] => Array ( [2013-07-25] => 2 [2013-07-23] => 1 [2013-07-21] => 3 ) ) [product3] => Array ( [dates] => Array ( [2013-07-24] => 5 [2013-07-22] => 4 [2013-07-20] => 3 ) ) [product1] => Array ( [dates] => Array ( [2013-07-29] => 1 [2013-07-28] => 2 [2013-07-27] => 2 ) ) ) I'd like to do a double sort: First, by productX ascending Then, for each product, by date of rent ascending So that the resulting array would be: Array ( [product1] => Array ( [dates] => Array ( [2013-07-27] => 2 [2013-07-28] => 2 [2013-07-29] => 1 ) ) [product2] => Array ( [dates] => Array ( [2013-07-21] => 3 [2013-07-23] => 1 [2013-07-25] => 2 ) ) [product3] => Array ( [dates] => Array ( [2013-07-20] => 3 [2013-07-22] => 4 [2013-07-24] => 5 ) ) ) How can I reach this? Many thanks in advance
try this: ksort($rent); foreach($rent as &$item) { ksort($item['dates']); }
You can simply ksort the products, then iterate through them and use the same for the dates key. ksort($products); foreach($products as &$product) ksort($product['dates']); Where $products is the array you showed us. Note that you need to pass the value in the foreach loop as a reference (using the & operator) otherwise the changes won't be updated in the original array.
for my understanding of your problem; Nadh solution is almost there. but i believe you want ksort() this is my corrections to Nadh answer ksort($rent); foreach($rent as $product => $dates) { ksort($rent[$product]['dates']); } print_r($rent);