I need to group my subarray using product_id and perform some conditional arithmetic.
If moving_type is 1 then the overall product_number tally for the current product_id should be increased by the product_number. Alternatively, if the moving_type is 2 then the overall product_number tally should be decreased by the product_number value.
This is my input array:
Array
(
[0] => Array
(
[id] => 1
[product_id] => 4
[product_number] => 10
[moving_type] => 1
)
[1] => Array
(
[id] => 1
[product_id] => 5
[product_number] => 10
[moving_type] => 1 // product addition
)
[2] => Array
(
[id] => 1
[product_id] => 5
[product_number] => 2
[moving_type] => 2 // product minus
)
)
My desired result is:
Array
(
[0] => Array
(
[id] => 1
[product_id] => 4
[product_number] => 10
)
[1] => Array
(
[id] => 1
[product_id] => 5
[product_number] => 8 // as 10-2
)
)
Most efficiently use temporary keys based on product_id to determine if you are performing arithmetic between two values or merely declaring the first occurring value for the product_id.
If it is the first occurrence, just slice the first three rows off the row and preserve the keys (that's the true part).
If it is not the first occurrence of product_id, then you will only need to update the product_number element. Determine the correct operation based on the moving_type value -- using that, multiply the product_number by either 1 or -1 and add that generated value to the stored value.
When the loop is finished, you can reindex the resulting array with array_values().
Code: (Demo)
$array = [
['id' => 1, 'product_id' => 4, 'product_number' => 10, 'moving_type' => 1],
['id' => 1, 'product_id' => 5, 'product_number' => 10, 'moving_type' => 1],
['id' => 1, 'product_id' => 5, 'product_number' => 2, 'moving_type' => 2]
];
foreach ($array as $row) {
if (!isset($result[$row['product_id']])) {
$result[$row['product_id']] = array_slice($row, 0, 3, true);
} else {
$result[$row['product_id']]['product_number'] += ($row['moving_type'] == 1 ? 1 : -1) * $row['product_number'];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => 1,
'product_id' => 4,
'product_number' => 10,
),
1 =>
array (
'id' => 1,
'product_id' => 5,
'product_number' => 8,
),
)
You could use a foreach() loop. Inside it, you could create a new array using product_id as key. You could use *=-1 to change the sign of the product_number, and if the key already exists, add the current product_number to the existing one.
$array = array(
array('id' => 1, 'product_id' => 4, 'product_number' => 10, 'moving_type' => 1),
array('id' => 1, 'product_id' => 5, 'product_number' => 10, 'moving_type' => 1),
array('id' => 1, 'product_id' => 5, 'product_number' => 2, 'moving_type' => 2)
);
$final = [];
foreach ($array as $item) {
$pid = $item['product_id'] ;
if ($item['moving_type'] == 2) $item['product_number'] *= -1;
unset($item['moving_type']);
if (!isset($final[$pid])) { $final[$pid] = $item ; }
else {
$final[$pid]['product_number'] += $item['product_number'] ;
}
}
$final = array_values($final);
print_r($final);
Output:
Array
(
[0] => Array
(
[id] => 1
[product_id] => 4
[product_number] => 10
)
[1] => Array
(
[id] => 1
[product_id] => 5
[product_number] => 8
)
)
Related
I have an array with the following structure:
[0] => Array
(
[venue1] => 1
[venue2] => 2
)
[1] => Array
(
[venue1] => 3
[venue2] => 4
)
[2] => Array
(
[venue1] => 2
[venue2] => 1
)
[3] => Array
(
[venue1] => 5
[venue2] => 6
)
I need to remove the duplicate "pair of values", in this case row [0] and row [2]
I tried it with that code, but it doesn't work (and of course it's not very elegant) ;-)
foreach ( $compare_arr as $v1 )
{
$key = array_search( intval($v1[venue1]), array_column( $compare_arr, 'venue2' ) );
if ( $key <> '' ) unset($compare_arr[$key]);
}
Do you have an idea how to solve this?
Thanks a lot for your help!
Oliver
Here is an approach where an intermediate array is formed of sorted values. That you can then search for to find duplicate pairs to remove.
<?php
$venues =
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 2,
'venue2' => 1,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
);
$result = $pairs = $venues;
array_walk($pairs, 'sort');
var_export($pairs);
foreach($pairs as $k => $pair) {
if(count(array_keys($pairs, $pair)) > 1) {
unset($result[$k]);
}
}
var_export($result);
Output:
array (
0 =>
array (
0 => 1,
1 => 2,
),
1 =>
array (
0 => 3,
1 => 4,
),
2 =>
array (
0 => 1,
1 => 2,
),
3 =>
array (
0 => 5,
1 => 6,
),
)array (
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
If you want to remove occurring duplicates rather than pruning out duplicates altogether, you can do an array_unique on the sorted array above and then use the remaining keys to filter the original array.
$tmp = $venues;
array_walk($tmp, 'sort');
$tmp = array_unique($tmp, SORT_REGULAR);
$result = array_intersect_key($venues, $tmp);
var_export($result);
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
You might also first loop the array creating a compound key based on the ordered keys.
Then you can filter the result only keeping arrays where the count is 2 as nothing is added because there are no duplicates.
For example
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6],
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
$result[$cKey][] = $v1;
continue;
}
$result[$cKey] = $v1;
}
$result = array_filter($result, function($item) {
return count($item) === 2;
});
print_r($result);
Output
Array
(
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
You can see the compound keys are the values with a - in between. If you want to have the keys numbered from 0, you can use array_values.
Php demo
Edit
If you want to keep the first matching single pair, you can check for the compound key and if it already exists continue the loop without overwriting the existing one.
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6]
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
continue;
}
$result[$cKey] = $v1;
}
print_r($result);
Output
Array
(
[1-2] => Array
(
[0] => 1
[1] => 2
)
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
Php demo
Whether you use a classic foreach() loop or functional iteration, there is no reason to iterate the input array more than once.
This snippet will appear nearly identical to TheFourthBird's answer, but I don't like the unnecessary use of continue. This snippet will ensure no that rows in the result array have 100% shared venue values (in any order). The subarray keys will also not suffer reordering; in other words the first element key will be venue1 then the second element will be venue2. Using implode() offers additional flexibility because the code won't need to be altered if the number of elements in each row changes.
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($result[$key])) {
$result[$key] = $data[$index];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
To completely remove all rows where venue values are shared, maintain a "found" array as well as a "result" array.
Code: (Demo)
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($found[$key])) {
$found[$key] = true;
$result[$key] = $data[$index];
} else {
unset($result[$key]);
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 3,
'venue2' => 4,
),
1 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
I have below array $billitems_taxes
[0] => Array
(
[id] => 1
[tax_name] => A
[tax_value] => 12
)
[1] => Array
(
[id] => 2
[tax_name] => A
[tax_value] => 8
)
[2] => Array
(
[id] => 3
[tax_name] => B
[tax_value] => 12
)
and I want output as below, find two common tax_name and do some of same and then create a new array.
[0] => Array
(
[id] => 1
[tax_name] => A
[tax_value] => 20
)
[1] => Array
(
[id] => 3
[tax_name] => B
[tax_value] => 12
)
I tried with below code, but it did not return a correct array.
$return_array = [];
foreach($billitems_taxes as $b)
{
$return_array['tax_name'] = $b->tax_name;
$return_array['tax_value'] += $b->tax_value;
}
First off, you have an array of arrays, not objects.
Then your loop needs to know if it has already seen a this tax name which will already be in the new array to check that I used array_key_exists()
$return_array = [];
foreach($billitems_taxes as $b)
{
if ( array_key_exists($b['tax_name'], $return_array) ) {
$return_array[$b['tax_name']]['tax_value'] += $b['tax_value'];
} else {
$return_array[$b['tax_name']] = $b;
}
}
RESULT
Array(
[A] => Array
([id] => 1
[tax_name] => A
[tax_value] => 20
)
[B] => Array
([id] => 3
[tax_name] => B
[tax_value] => 12
)
)
And if its important for the array to be numerically indexed just add
$return_array = array_values($return_array);
after the end of the loop
You must group by 'tax_name' and must sum 'tax_value'.
$billitems_taxes = [
['id' => 1, 'tax_name' => 'A', 'tax_value' => 12],
['id' => 2, 'tax_name' => 'A', 'tax_value' => 8],
['id' => 3, 'tax_name' => 'B', 'tax_value' => 12]
];
$result = [];
foreach($billitems_taxes as $row){
$groupKey = $row['tax_name'];
if(array_key_exists($groupKey,$result)){
$result[$groupKey]['tax_value'] += $row['tax_value'];
} else {
$result[$groupKey] = $row;
}
}
$result = array_values($result);
echo '<pre>';
var_export($result);
/*
array (
0 =>
array (
'id' => 1,
'tax_name' => 'A',
'tax_value' => 20,
),
1 =>
array (
'id' => 3,
'tax_name' => 'B',
'tax_value' => 12,
),
)
*/
The solution with the external class tableArray is very simple. The result is the same.
$result = tableArray::create($billitems_taxes)
->filterGroupAggregate(['tax_value' => 'SUM'],['tax_name'])
->fetchAll()
;
I would like to combine 2 arrays into 1 in PHP or laravel. I've searched this site for similar questions but can't seem to find an answer.
Can someone help me with this?
**array 1 -- $insertData **
Array
(
[0] => Array
(
[prid] => 4
[vendor_id] => 1
)
[1] => Array
(
[prid] => 5
[vendor_id] => 2
)
)
**Array - 2 $requestData **
Array
(
[vendor_id] => Array
(
[0] => 1
[1] => 1
[2] => 2
[3] => 2
)
[item] => Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 5
)
[qty] => Array
(
[0] => 12
[1] => 13
[2] => 14
[3] => 15
)
)
**Required Output ---- how can I do this array1 and array2 combine into a single array **
Array
(
[0] => Array
(
[prid] => 4
[vendor_id] => 1
[item] => 2
[qty] => 12
)
[1] => Array
(
[prid] => 4
[vendor_id] => 1
[item] => 3
[qty] => 13
)
[2] => Array
(
[prid] => 5
[vendor_id] => 2
[item] => 4
[qty] => 14
)
[3] => Array
(
[prid] => 5
[vendor_id] => 2
[item] => 5
[qty] => 15
)
)
My controller
public function prtmulti(Request $req)
{
$maxPrId = newpr::max('prid');
// print_r($maxPrId);
echo "<pre>";
$requestData = $req->all();
if (array_key_exists("vendor_name", $requestData)) {
$insertData = [];
$uniqueData = array_unique($requestData["vendor_name"]);
foreach ($uniqueData as $key => $value) {
$maxId = $maxPrId+1;
$insertData[] = ['prid' => $maxId, 'vendor_id' => $value];
$maxPrId = $maxPrId+1;
}
}
print_r($insertData);
print_r($requestData);
}
you can achieve this using the array_combine function in php, for example:
<?php
$fname=array("Peter","Ben","Joe");
$age=array("35","37","43");
$c=array_combine($fname,$age);
print_r($c);
?>
I'm pretty sure that Laravel doesn't offer anything out of the box to execute your desired merging technique (and I don't see why it would bother).
Assuming that the vendor_id values in the first array are unique, you will get best performance by creating a lookup array. array_column() can be used to declare an array with vendor_id values as keys and prid values as values.
Because your $requestData has rows with the number of columns desired in the output, loop over the $requestData['vendor_id'] data and manually generate the desired rows of data in the result array.
Code: (Demo)
$insertData = [
['prid' => 4, 'vendor_id' => 1],
['prid' => 5, 'vendor_id' => 2],
];
$requestData = [
'vendor_id' => [1, 1, 2, 2],
'item' => [2, 3, 4, 5],
'qty' => [12, 13, 14, 15]
];
$insertLookup = array_column($insertData, 'prid', 'vendor_id');
$result = [];
foreach ($requestData['vendor_id'] as $index => $vendorId) {
$result[] = [
'prid' => $insertLookup[$vendorId],
'vendor_id' => $vendorId,
'item' => $requestData['item'][$index],
'qty' => $requestData['qty'][$index],
];
}
var_export($result);
Output:
array (
0 =>
array (
'prid' => 4,
'vendor_id' => 1,
'item' => 2,
'qty' => 12,
),
1 =>
array (
'prid' => 4,
'vendor_id' => 1,
'item' => 3,
'qty' => 13,
),
2 =>
array (
'prid' => 5,
'vendor_id' => 2,
'item' => 4,
'qty' => 14,
),
3 =>
array (
'prid' => 5,
'vendor_id' => 2,
'item' => 5,
'qty' => 15,
),
)
You can use the array_merge() function to merge arrays.
array_merge
$merged_array = array_merge($insertData, $requestData);
I try to use array_column and array_multisort to sort Array B by Key (ID).
However, I have a scenario whereby I need to have some ID to be sticky on the top .
For example, by comparing Array A and Array B, move ID 3 and ID 1 to the top of the Array B. The final result will be Array C .
Is there a PHP array function to achieve this? Please advice...
Array A
Array
(
[0] => Array
(
[ID] => 3
)
[1] => Array
(
[ID] => 1
)
)
1
Array B
Array
(
[0] => Array
(
[ID] => 1
[product] => A
)
[1] => Array
(
[ID] => 2
[product] => B
)
[2] => Array
(
[ID] => 3
[product] => C
)
[3] => Array
(
[ID] => 4
[product] => D
)
)
1
Array C (Result)
Array
(
[0] => Array
(
[ID] => 3
[product] => C
)
[1] => Array
(
[ID] => 1
[product] => A
)
[2] => Array
(
[ID] => 2
[product] => B
)
[3] => Array
(
[ID] => 4
[product] => D
)
)
1
The preparatory steps involved in generating the lookup and outlier values may be avoidable with more intimate knowledge of your project scope, but I'll only use the values that you have provided. (I mean, you could hardcode outlier as 9999999 and wrap array_flip around array_column for brevity.)
The max value simply needs to be a value higher than the highest ID in the priority array.
The lookup needs to have ID values as keys and their original indexes as the new values (hence the flip).
usort() is precisely the php function to use when performing a custom sorting process.
use is necessary to pass the required additional values into the custom function's scope.
The spaceship operator (<=>) is a fantastic way to package your two-condition sort logic. Each side will be compared using the values "left to right".
Code: (Demo)
$priority = [['ID' => 3], ['ID' => 1]];
$input = [['ID' => 1, 'product' => 'A'], ['ID' => 2, 'product' => 'B'], ['ID' => 3, 'product' => 'C'], ['ID' => 4, 'product' => 'D']];
$lookup = array_column($priority, 'ID');
$outlier = max($lookup) + 1;
$lookup = array_flip($lookup);
usort($input, function($a, $b) use ($lookup, $outlier) {
return [$lookup[$a['ID']] ?? $outlier, $a['ID']] <=> [$lookup[$b['ID']] ?? $outlier, $b['ID']];
});
var_export($input);
Output:
array (
0 =>
array (
'ID' => 3,
'product' => 'C',
),
1 =>
array (
'ID' => 1,
'product' => 'A',
),
2 =>
array (
'ID' => 2,
'product' => 'B',
),
3 =>
array (
'ID' => 4,
'product' => 'D',
),
)
Without the spaceship operator, the custom function gets noticeably more verbose.
Code: (Demo)
usort($input, function($a, $b) use ($lookup, $outlier) {
$bIsPriority = isset($lookup[$b['ID']]);
if (isset($lookup[$a['ID']])) {
if ($bIsPriority) {
return $lookup[$a['ID']] - $lookup[$b['ID']];
} else {
return -1;
}
} elseif ($bIsPriority) {
return 1;
} else {
return $a['ID'] - $b['ID'];
}
});
Here is one of doing it:
<?php
$a = [['ID' => 5], ['ID' => 1]];
$b = [['ID' => 1, 'product' => 'A'], ['ID' => 2, 'product' => 'B'], ['ID' => 3, 'product' => 'C'], ['ID' => 4, 'product' => 'D'], ['ID' => 5, 'product' => 'E']];
// $keysOnTop = Array([0] => 5, [1] => 1)
$keysOnTop = array_column($a, 'ID');
$temp1 = $temp2 = [];
foreach($b as $value){
if(in_array($value['ID'], $keysOnTop)){
$temp1[] = $value;
} else {
$temp2[] = $value;
}
}
// $temp1 = Array([0] => Array([ID] => 1, [product] => A), [1] => Array([ID] => 5, [product] => E))
// $temp2 = Array([0] => Array([ID] => 2, [product] => B), [1] => Array([ID] => 3, [product] => C), [2] => Array([ID] => 4, [product] => D))
$final_arr = array_merge($temp1, $temp2);
echo '<pre>';
print_r($final_arr);
// Output:
/*
Array
(
[0] => Array
(
[ID] => 1
[product] => A
)
[1] => Array
(
[ID] => 5
[product] => E
)
[2] => Array
(
[ID] => 2
[product] => B
)
[3] => Array
(
[ID] => 3
[product] => C
)
[4] => Array
(
[ID] => 4
[product] => D
)
)
*/
?>
This is a section of my array:
[1] => Array
(
[quantity] => 2
[product_id] => 1
[option_id] => 22
)
[2] => Array
(
[quantity] => 2
[product_id] => 2
[option_id] => 22
)
[3] => Array
(
[quantity] => 3
[product_id] => 2
[option_id] => 22
)
[4] => Array
(
[quantity] => 1
[product_id] => 2
[option_id] => 25
)
I wish to group/merge the subarrays by product_id and option_id.
Upon merging subarrays, I would like to sum the quantity values.
In my sample data, both subarrays [2] and [3] have 'product_id'=>2 and 'option_id'=>22. They should be merged together into one subarray with a quantity value of 5.
This is my expected output:
[1] => Array
(
[quantity] => 2
[product_id] => 1
[option_id] => 22
)
[2] => Array
(
[quantity] => 5
[product_id] => 2
[option_id] => 22
)
[3] => Array
(
[quantity] => 1
[product_id] => 2
[option_id] => 25
)
*My first level keys are not associated with their subarrays so they may be changed in the process. I do want the first level keys to be incremented from 1 not 0.
You only need to build compound temporary keys and then overwrite the keys.
By writing an initial element (null in this case) then deleting the element after the loop is finished, you ensure that the first key in the result array is 1.
To avoid the "messiness" of the repeated long-winded compound key, you can save the the dynamic key as a variable in the first line inside of the foreach loop, then reference that variable in the three respective locations in the condition block.
Code (Demo)
$array = [
1 => ['quantity' => 2, 'product_id' => 1, 'option_id' => 22],
2 => ['quantity' => 2, 'product_id' => 2, 'option_id' => 22],
3 => ['quantity' => 3, 'product_id' => 2, 'option_id' => 22],
4 => ['quantity' => 1, 'product_id' => 2, 'option_id' => 25]
];
$result = [null]; // create placeholding element
foreach($array as $subarray){
$composite_key = $subarray['product_id'] . '_' . $subarray['option_id'];
if(!isset($result[$composite_key])){
$result[$composite_key] = $subarray; // first occurrence
}else{
$result[$composite_key]['quantity'] += $subarray['quantity']; // not first occurrence
}
}
$result=array_values($result); // change from assoc to indexed
unset($result[0]); // remove first element to start numeric keys at 1
var_export($result);
Output:
array (
1 =>
array (
'quantity' => 2,
'product_id' => 1,
'option_id' => 22,
),
2 =>
array (
'quantity' => 5,
'product_id' => 2,
'option_id' => 22,
),
3 =>
array (
'quantity' => 1,
'product_id' => 2,
'option_id' => 25,
),
)
One possible solution to your problem consists of the following steps:
Use a double for loop to examine every element in the array with every element after it.
Compare the product and option ids and if they match.
Sum the quantities into the former item.
Unset the latter item.
Use array_values to make the array compact again.
Break out of the nested loop.
Code:
# The sample array.
$array = [
["quantity" => 2, "product_id" => 1, "option_id" => 22],
["quantity" => 2, "product_id" => 2, "option_id" => 22],
["quantity" => 3, "product_id" => 2, "option_id" => 22],
["quantity" => 1, "product_id" => 2, "option_id" => 25]
];
# Iterate over every element of the array.
for ($i = 0, $l = count($array); $i < $l; $i++) {
# Iterate over every element after the current one in the array.
for ($j = $i + 1; $j < $l; $j++) {
# Compare the product and option ids.
$sameProduct = $array[$i]["product_id"] == $array[$j]["product_id"];
$sameOption = $array[$i]["option_id"] == $array[$j]["option_id"];
# Check whether two items have the same ids.
if ($sameProduct && $sameOption) {
# Add the value of the item at index j to the one at index i.
$array[$i]["quantity"] += $array[$j]["quantity"];
# Unset the array at index j.
unset($array[$j]);
# Make the array compact.
$array = array_values($array);
# Break out of the loops.
break 2;
}
}
}
Note: Your question shows no effort made in your part. I'm assuming you are completely lost and have no idea what to do and that's the only reason I'm answering. If you have made any effort that did not work as expected include it in your question, as that will prompt other users to answer too and perhaps give you better answers than mine.