sort array by multiple keys with optional key - php

The following array contains a sample of data from a larger data set -
$data =
[
[
'readingSequence' => '35',
'bookID' => '19',
'chapter' => '130'
],
[
'readingSequence' => '19',
'bookID' => '19',
'chapter' => '27'
],
[
'readingSequence' => '59',
'bookID' => '19',
'chapter' => '133'
],
[
'readingSequence' => '35',
'bookID' => '19',
'chapter' => '129'
],
[
'readingSequence' => '98',
'bookID' => '75',
'chapter' => '6',
'verses' => '11-20,',
'versesFirst' => '11'
],
[
'readingSequence' => '99',
'bookID' => '75',
'chapter' => '6',
'verses' => '1-10',
'versesFirst' => '1'
]
];
The use case requires that the elements of $data be sorted by bookID, then chapter, then versesFirst if that key exists.
The following code successfully sorts the array by bookID and chapter but I haven't figured out a way to also sort by versesFirst because it is present in some elements but not in others -
array_multisort(array_column($data, 'bookID'), SORT_ASC, array_column($data, 'chapter'), SORT_ASC, $data);
Here is the result:
Array
(
[0] => Array
(
[readingSequence] => 19
[bookID] => 19
[chapter] => 27
)
[1] => Array
(
[readingSequence] => 35
[bookID] => 19
[chapter] => 129
)
[2] => Array
(
[readingSequence] => 35
[bookID] => 19
[chapter] => 130
)
[3] => Array
(
[readingSequence] => 59
[bookID] => 19
[chapter] => 133
)
[4] => Array
(
[readingSequence] => 98
[bookID] => 75
[chapter] => 6
[verses] => 11-20,
[versesFirst] => 11
)
[5] => Array
(
[readingSequence] => 99
[bookID] => 75
[chapter] => 6
[verses] => 1-10
[versesFirst] => 1
)
)
Elements 0 to 3 are sorted correctly but elements 4 and 5 are not because versesFirst is not included. When an attempt was made to include it this warning was displayed and the results looked the same as shown above.
Warning: array_multisort(): Array sizes are inconsistent in C:\development\scratchpad.php on line 122
I could add [versesFirst] => '' to the elements that don't have the versesFirst key so that it could be included in the multisort but I'd rather not resort to that. Is there a way to include optional keys in the multisort? Or is there a different approach entirely to achieve the desired result set?

You can use usort to accomplish this with isset checks for versesFirst key.
Snippet:
<?php
usort($data, function($a,$b){
$book_id_1 = intval($a['bookID']);
$book_id_2 = intval($b['bookID']);
if($book_id_1 != $book_id_2) return $book_id_1 <=> $book_id_2;
$chapter_1 = intval($a['chapter']);
$chapter_2 = intval($b['chapter']);
if($chapter_1 != $chapter_2) return $chapter_1 <=> $chapter_2;
if(!isset($b['versesFirst'])){
return -1;
}
if(!isset($a['versesFirst'])){
return 1;
}
return intval($a['versesFirst']) <=> intval($b['versesFirst']);
});
print_r($data);

Related

sum value in foreach loop based on another value php

I have an array like below. There are id, label, cost, and cid in an array. We want to sum the cost based on the cid like for cid 22 cost should be 196.5 and for cid 11 cost should be 44.4. In our out put array we want to keep the label, cost (sum), and cid, Please see expected output array.
Array
(
[0] => Array
(
[id] => 1331
[label] => PM1
[cost] => 98.25
[cid] => 22
[product_id] => 133
)
[1] => Array
(
[id] => 1332
[label] => PM3
[cost] => 22.20
[cid] => 11
[product_id] => 133
)
[2] => Array
(
[id] => 1341
[label] => PM1
[cost] => 98.25
[cid] => 22
[product_id] => 134
)
[3] => Array
(
[id] => 1342
[label] => PM3
[cost] => 22.20
[cid] => 11
[product_id] => 134
)
)
Tried below
foreach ($array $key => $value) {
$final[$value['cid']] += $value['cost'];
}
print_r ($final);
Getting below as an output
Array
(
[22] => 196.5
[11] => 44.4
)
Want expected output like below.
Array
(
[22] => Array
(
[label] => PM1
[cost] => 196.5
[cid] => 22
)
[11] => Array
(
[label] => PM3
[cost] => 44.4
[cid] => 11
)
)
Basically want to sum cost based on cid and want to keep the label, cost (sum), and cid.
Any help will be greatly appreciated.
I believe this should do the trick
$CIDs_identified = array();
$final_array = array();
foreach($original_array as $small_array){
if(!in_array($small_array['cid'], $CIDs_identified)){
$CIDs_identified[] = $small_array['cid'];
$final_array[$small_array['cid']] = array(
'label' => $small_array['label'],
'cost' => $small_array['cost'],
'cid' => $small_array['cid'],
);
}else{
$final_array[$small_array['cid']]['cost'] += $small_array['cost'];
}
}
On this site, if you can provide your source array in usable format, that is really helpful so that we don't have to retype it. One way to do that is using var_export.
Below is a simple version. Create an array indexed by cid. If that item doesn't exist, populate it with the basic data and a zero cost. Then on each loop, including the initial, add the row's cost.
<?php
$data = [
[
'id' => 1331,
'label' => 'PMI',
'cost' => 98.25,
'cid' => 22,
'product_id' => 133,
],
[
'id' => 1341,
'label' => 'PMI',
'cost' => 98.25,
'cid' => 22,
'product_id' => 134,
],
];
$output = [];
foreach ($data as $item) {
if (!isset($output[$item['cid']])) {
$output[$item['cid']] = [
'label' => $item['label'],
'cost' => 0,
'cid' => $item['cid'],
];
}
$output[$item['cid']]['cost'] += $item['cost'];
}
print_r($output);
Demo here: https://3v4l.org/MY6Xu
$list = [
[ 'id' => 1331, 'label' => 'PM1', 'cost' => 98.25, 'cid' => 22, 'product_id' => 133 ],
[ 'id' => 1332, 'label' => 'PM3', 'cost' => 22.20, 'cid' => 11, 'product_id' => 133 ],
[ 'id' => 1341, 'label' => 'PM1', 'cost' => 98.25, 'cid' => 22, 'product_id' => 134 ],
[ 'id' => 1342, 'label' => 'PM3', 'cost' => 22.20, 'cid' => 11, 'product_id' => 134 ]
];
$result = [];
array_walk($list, function ($item) use (&$result) {
if (isset($result[$item['cid']])) {
$result[$item['cid']]['cost'] = $item['cost'] + $result[$item['cid']]['cost'];
} else {
$result[$item['cid']] = [ 'label' => $item['label'], 'cost' => $item['cost'], 'cid' => $item['cid'] ];
}
});
print_r($result);
Output:
Array
(
[22] => Array
(
[label] => PM1
[cost] => 196.5
[cid] => 22
)
[11] => Array
(
[label] => PM3
[cost] => 44.4
[cid] => 11
)
)

Unable to merge nested arrays data in php

I try to merge the data's in the arrays in 'c' and 'a' inside MYDATA but is it possible? Some examples and tips would be helpful! I would love to hear from you!
[0] => Array
(
[MYDATA] => Array
(
[id] => 79
[my_birth_day] => 1990-06-20
[my_address] => 400
[my_age] => 26
[my_name] => Joy
[my_id] => 1
[created] => 2017-06-19 15:39:44
)
[c] => Array
(
[my_test] => math
)
[a] => Array
(
[my_date] => 2017-08-13
)
)
I would want the result to be like
[MYDATA] => Array
(
[id] => 79
[my_birth_day] => 1990-06-20
[my_address] => 400
[my_age] => 26
[my_name] => Joy
[my_id] => 1
[created] => 2017-06-19 15:39:44
[my_test] => math
[my_date] => 2017-08-13
Just merge the elements one by one:
<?php
$input = [
'MYDATA' => [
'id' => 79,
'my_birth_day' => '1990-06-20',
'my_address' => 400,
'my_age' => 26,
'my_name' => 'Joy',
'my_id' => 1,
'created' => '2017-06-19 15:39:44'
],
'c' => [
'my_test' => 'math'
],
'a' => [
'my_date' => '2017-08-13'
]
];
$output = [];
array_walk($input, function($element) use (&$output) {
$output = array_merge($output, $element);
});
print_r($output);
The order in which you iterate over the input array determines which entries will win in case you have key collisions.
The output obviously is:
Array
(
[id] => 79
[my_birth_day] => 1990-06-20
[my_address] => 400
[my_age] => 26
[my_name] => Joy
[my_id] => 1
[created] => 2017-06-19 15:39:44
[my_test] => math
[my_date] => 2017-08-13
)
The function needed is array_merge(), it takes an unlimited number of parameters, the order of the parameters dictates the order in which the arrays are merged, in this case it would not matter, but say you had matching keys, say two 'my_address' keys the value from the last array defining this key will be returned as a result as the function call.
See the docs here: http://php.net/manual/en/function.array-merge.php
Merge the inner arrays, assuming your outermost array is called $array you can try the following:
<?php
$array = array(
'MYDATA' => array(
'id' => '79',
'my_birth_day' => '1990-06-20',
'my_address' => '400',
'my_age' => '26',
'my_name' => 'Joy',
'my_id' => '1',
'created' => '2017-06-19 15:39:44'
),
'c' => array(
'my_test' => 'math'
),
'a' => array(
'my_date' => '2017-08-13'
)
);
$mergedMyData = array_merge($array['MYDATA'], $array['c'], $array['a']);
//output the data
print_r($mergedMyData);
?>
hope this helps

Group objects in php with similar course_id

I have a variable $items that has following data inside it.
Array
(
[0] => stdClass Object
(
[course_id] => 8
[doc_id] => 85
[doc_title] => PDF Notes
[doc_price] => 243
)
[1] => stdClass Object
(
[course_id] => 6
[doc_id] => 73
[doc_title] => PDF Notes
[doc_price] => 12
)
[2] => stdClass Object
(
[course_id] => 6
[doc_id] => 75
[doc_title] => Audio Topics
[doc_price] => 21
)
[3] => stdClass Object
(
[course_id] => 6
[doc_id] => 77
[doc_title] => Video Past Papers
[doc_price] => 32
)
)
I want to categorize the data. The course_id should become the key and the courses that have similar course_id should be categorized and become the value of that key. Like in the about array, [1], [2] and [3] have same course_id so the for key => 6, an array should be made with sub array that have docs 73, 75 and 77.
array => (
'6' => 'array of docs with course_id 6',
'8' => 'array of docs with course_id 8',
)
VAR_EXPORT
array (
0 =>
stdClass::__set_state(array(
'course_id' => '8',
'doc_id' => '85',
'doc_title' => 'PDF Notes',
'doc_price' => '243',
)),
1 =>
stdClass::__set_state(array(
'course_id' => '6',
'doc_id' => '73',
'doc_title' => 'PDF Notes',
'doc_price' => '12',
)),
2 =>
stdClass::__set_state(array(
'course_id' => '6',
'doc_id' => '75',
'doc_title' => 'Audio Topics',
'doc_price' => '21',
)),
3 =>
stdClass::__set_state(array(
'course_id' => '6',
'doc_id' => '77',
'doc_title' => 'Video Past Papers',
'doc_price' => '32',
)),
)
Try this:
use get_object_vars to convert obj to array.
$newarray = array();
foreach ($items as $item)
{
$newarray[$item->course_id][] = get_object_vars($item);
}

How to get unique ids from array loop using php

I have the following array which I am getting as mysql result set
Array
(
[0] => Array
(
[slug] => block_three_column
[title] => CSG 2
[type_id] => 8
[entry_id] => 6
[stream_id] => 11
)
[1] => Array
(
[slug] => block_three_column
[title] => CSG
[type_id] => 8
[entry_id] => 5
[stream_id] => 11
)
)
Array
(
[0] => Array
(
[slug] => block_three_column
[title] => CSG 2
[type_id] => 8
[entry_id] => 6
[stream_id] => 11
)
[1] => Array
(
[slug] => block_three_column
[title] => CSG
[type_id] => 8
[entry_id] => 5
[stream_id] => 11
)
)
The both arrays are similar I want get the unique entry id using php.
I tried with the following code but it is producing 2 arrays again.
foreach($block_results as $rowarr)
{
foreach($rowarr as $k=>$v)
{
if($k == "entry_id")
{
$entid[] = $v;
}
}
}
Any help is highly appreciated. Thanks in advance.

You can use array_map() instead of foreach(). Example:
$entry_ids = array_unique(
array_map(
function($v){
return $v['entry_id'];
},
$array
)
);
var_dump($entry_ids);
array_unique() is to remove duplicate elements from array().
You can get the entry_id directly:
$array = array(
array(
'slug' => 'block_three_column',
'title' => 'CSG 2',
'type_id' => 8,
'entry_id' => 6,
'stream_id' => 11
),
array(
'slug' => 'block_three_column',
'title' => 'CSG',
'type_id' => 8,
'entry_id' => 5,
'stream_id' => 11
)
);
foreach ($array as $innerArray) {
if (isset($innerArray['entry_id'])) {
$entid[] = $innerArray['entry_id'];
}
}
var_dump($entid);
Output is:
array
0 => int 6
1 => int 5
Assuming that you want a list of the unique values of entry_id from the array, and it is possible that some will not have an entry_id set:
$entid = array();
foreach ( $array as $blockArray) {
if ( isset( $blockArray['entry_id'] )
&& !in_array( $blockArray['entry_id'], $entid ) ) {
$entid[] = $blockArray['entry_id'];
}
}
var_dump( $entid );
Try this:
PHP
$entid = array(); //Holds unique id's
foreach($block_results as $rowarr)
{
//Make sure entry_id exists
$entid[] = array_key_exists('entry_id',$rowarry) ? $rowarr['entry_id'] : false;
}
If they are always similiar and the same length:
foreach ($array1 as $key => $value) {
$firstValue = $array1[$key]['entry_id'];
$secondValue = $array2[$key]['entry_id'];
}
Though, keep in mind this solution is very sensitive to errors.
try this like your code:
$block_results = array(
array(
'slug' => 'block_three_column',
'title' => 'CSG 2',
'type_id' => 8,
'entry_id' => 6,
'stream_id' => 11
),
array(
'slug' => 'block_three_column',
'title' => 'CSG',
'type_id' => 8,
'entry_id' => 6,
'stream_id' => 11
),
array(
'slug' => 'block_three_column',
'title' => 'CSG',
'type_id' => 8,
'entry_id' => 7,
'stream_id' => 11
)
);
var_dump($block_results);
$entid=array();
if(count($block_results)>0)foreach($block_results as $rowarr)
{
$entid[] = $rowarr['entry_id'];
}
$entid=array_unique($entid);
var_dump($entid);
output:
array
0 =>
array
'slug' => string 'block_three_column' (length=18)
'title' => string 'CSG 2' (length=5)
'type_id' => int 8
'entry_id' => int 6
'stream_id' => int 11
1 =>
array
'slug' => string 'block_three_column' (length=18)
'title' => string 'CSG' (length=3)
'type_id' => int 8
'entry_id' => int 6
'stream_id' => int 11
2 =>
array
'slug' => string 'block_three_column' (length=18)
'title' => string 'CSG' (length=3)
'type_id' => int 8
'entry_id' => int 7
'stream_id' => int 11
array
0 => int 6
2 => int 7

Have the following array merged

I have the following array:
Array
(
[0] => Array
(
[Vendor_ID] => 1
[Quantity] => 55
)
[1] => Array
(
[Vendor_ID] => 1
[Quantity] => 55
)
[2] => Array
(
[Vendor_ID] => 1
[Quantity] => 55
)
[3] => Array
(
[Vendor_ID] => 3
[Quantity] =>
)
[4] => Array
(
[Vendor_ID] => 3
[Quantity] =>
)
[5] => Array
(
[Vendor_ID] => 3
[Quantity] =>
)
[6] => Array
(
[Vendor_ID] => 4
[Quantity] =>
)
[7] => Array
(
[Vendor_ID] => 4
[Quantity] =>
)
[8] => Array
(
[Vendor_ID] => 4
[Quantity] =>
)
)
Which is being created with the following code:
$Display_Arr = array();
$Tick = 0;
foreach ($_POST['product'] AS $_1){
if (!in_array($_1['vendor_id'], $Display_Arr)){
$Display_Arr[$Tick] = array(
"Vendor_ID" => $_1['vendor_id'],
"Quantity" => ""
);
$Display_Arr[$Tick]["Quantity"] .= $_1['quantity'];
}else{
$Display_Arr[$Tick]["Quantity"] .= $_1['quantity'];
}
++$Tick;
}
echo "<pre>";
print_r($Display_Arr);
echo "</pre>";
But I am not getting my desired output, which is:
Array
(
[0] => Array
(
[Vendor_ID] => 1
[Quantity] => 55,55,55
)
[1] => Array
(
[Vendor_ID] => 3
[Quantity] =>
)
[2] => Array
(
[Vendor_ID] => 4
[Quantity] =>
)
)
Where am I going wrong with this?
#mathielo
The current output is:
Array
(
[1] => Array
(
[Vendor_ID] => 1
[Quantity] => 55
)
[3] => Array
(
[Vendor_ID] => 3
[Quantity] =>
)
[4] => Array
(
[Vendor_ID] => 4
[Quantity] =>
)
)
Whereas, i'm trying to obtain:
[0] => Array
(
[Vendor_ID] => 1
[Quantity] => 55,55,55
)
If I got it right, what you need is this:
EDIT: Just made some tests and got it right this time:
$Display_Arr = array();
foreach ($_POST['product'] AS $_1){
if (!array_key_exists($_1['Vendor_ID'], $Display_Arr)){
$Display_Arr[$_1['Vendor_ID']] = array(
"Vendor_ID" => $_1['Vendor_ID'],
"Quantity" => $_1['Quantity']
);
}else{
if(!empty($_1['Quantity']))
$Display_Arr[$_1['Vendor_ID']]["Quantity"] .= ",{$_1['Quantity']}";
}
}
echo "<pre>";
print_r($Display_Arr);
echo "</pre>";
The main problem was in if (!in_array($_1['vendor_id'], $Display_Arr)). PHP's in_array() checks for given needle in the array values, and we were trying to match to the Vendor_ID value stored in the outer array keys. That was fixed using array_key_exists().
EDIT 2: I used this for test data:
$_POST['product'] = array(
0 => array(
'Vendor_ID' => 1,
'Quantity' => 55
),
1 => array(
'Vendor_ID' => 1,
'Quantity' => 55
),
2 => array(
'Vendor_ID' => 1,
'Quantity' => 55
)
,
3 => array(
'Vendor_ID' => 3,
'Quantity' => ''
),
4 => array(
'Vendor_ID' => 3,
'Quantity' => ''
),
5 => array(
'Vendor_ID' => 3,
'Quantity' => ''
),
6 => array(
'Vendor_ID' => 4,
'Quantity' => ''
),
7 => array(
'Vendor_ID' => 4,
'Quantity' => ''
),
8 => array(
'Vendor_ID' => 4,
'Quantity' => ''
)
);
You won't be needing $Tick anymore, as you could use Vendor_ID as keys for the outer array.
Looks like the easiest way to solve this would be to first aggregate the vendor quantities, and then build the final array with the keys that you are using.
I am assuming the input looks something like this:
$_POST['product'] = [
['vendor_id' => 1, 'quantity' => 55],
['vendor_id' => 1, 'quantity' => 55],
['vendor_id' => 1, 'quantity' => 55],
['vendor_id' => 3, 'quantity' => null],
['vendor_id' => 3, 'quantity' => null],
['vendor_id' => 3, 'quantity' => null],
['vendor_id' => 4, 'quantity' => null],
['vendor_id' => 4, 'quantity' => null],
['vendor_id' => 4, 'quantity' => null],
];
Aggregating the quantities:
$aggregates = [];
foreach ($_POST['product'] as $product) {
$id = $product['vendor_id'];
if ( ! isset($aggregates[$id])) {
$aggregates[$id] = [];
}
if ($product['quantity'] > 0) {
$aggregates[$id][] = $product['quantity'];
}
}
The aggregates array should now look like this:
$aggregates = [
1 => [
0 => 55,
1 => 55,
2 => 55,
],
3 => [], // Empty
4 => [], // Empty
];
As you can see the data is now neatly organized and ready to be put into any format you want. Using the keys that you use in your question it is as simple as:
$output = [];
foreach ($aggregates as $vid => $qty) {
$quantity = implode(',', $qty);
$output[] = ['Vendor_ID' => $vid, 'Quantity' => $quantity];
}
The output should now look like this:
$output = [
['Vendor_ID' => 1, 'Quantity' => '55,55,55'],
['Vendor_ID' => 3, 'Quantity' => ''],
['Vendor_ID' => 4, 'Quantity' => ''],
];
This will output exactly what you are looking for although the output of the last answer (Sverri M. Olsen) is more useful. Here you get the quantities as a string while with Sverri's method you get an array in first place.
$Display_Arr = array();
$vendors=array();
foreach ($_POST['product'] AS $_1){
if (!in_array($_1['vendor_id'],$vendors)){
$vendors[]=$_1['vendor_id'];
$Display_Arr[sizeof($vendors)-1] = array(
"Vendor_ID" => $_1['vendor_id'],
"Quantity" => $_1['quantity']
);
}
else{
$vendorKey=array_search($_1['vendor_id'],$vendors);
$Display_Arr[$vendorKey]["Quantity"] .=(!empty($Display_Arr[$vendorKey]["Quantity"])?',':null).$_1['quantity'];
}
}

Categories