I have two arrays of products, both formatted exactly the same, like so:
$products = array(
[0] => array(
['product_id'] => 33
['variation_id'] => 0
['product_price'] => 500.00
),
[1] => array(
['product_id'] => 48
['variation_id'] => 0
['product_price'] => 600.00
),
)
I would like to be able to return a list of only those products not found in the second array, based on the product ID.
I only care about those NOT found in the second array, not additional ones added to the first, so array_diff won't seem to do the trick.
I suspect you want something like array_udiff. This lets you specify how to compare the two arrays, using a callback function. You just create a callback that compares based on product id's.
I think this satisfies what you want because the array_diff family of functions only compare the first array to the rest, it does not return elements that array2 (or 3, or 4) have that array1 does not.
<?php
$products = array(
0 => array(
'product_id' => 33,
'variation_id' => 0,
'product_price' => 500.00
),
1 => array(
'product_id' => 48,
'variation_id' => 0,
'product_price' => 600.00
)
);
$products2 = array(
1 => array(
'product_id' => 48,
'variation_id' => 0,
'product_price' => 600.00
),
2 => array(
'product_id' => 49,
'variation_id' => 0,
'product_price' => 600.00
)
);
function compare_ids($a, $b)
{
return $b['product_id'] - $a['product_id'];
}
var_dump(array_udiff($products, $products2, "compare_ids"));
?>
Outputs:
array(1) {
[0]=>
array(3) {
["product_id"]=>
int(33)
["variation_id"]=>
int(0)
["product_price"]=>
float(500)
}
}
A simple foreach loop should be enough:
<?php
$products = array(
0 => array(
'product_id' => 33,
'variation_id' => 0,
'product_price' => 500.00
),
1 => array(
'product_id' => 48,
'variation_id' => 0,
'product_price' => 600.00
)
);
$products2 = array(
1 => array(
'product_id' => 48,
'variation_id' => 0,
'product_price' => 600.00
),
2 => array(
'product_id' => 49,
'variation_id' => 0,
'product_price' => 600.00
)
);
$diff = array();
// Loop through all elements of the first array
foreach($products2 as $value)
{
// Loop through all elements of the second loop
// If any matches to the current element are found,
// they skip that element
foreach($products as $value2)
{
if($value['product_id'] == $value2['product_id'])
continue 2;
}
// If no matches were found, append it to $diff
$diff[] = $value;
}
The $diff array would then only hold the following value:
array (
0 =>
array (
'product_id' => 49,
'variation_id' => 0,
'product_price' => 600,
),
)
Hope this helped!
Related
I have two arrays, $array1 and $array2. $array2 is an array of arrays. For each subarray of $array2, I want to print $array1 but with additional entries that depend on whether the $array2 subarray has keys "a" or "c" with value 1. My code prints $array1 each loop, but there are additional entries in $array1 for the later iterations that I wasn't expecting. Why do I get these entries, and how do I prevent them?
Sample code:
$array1 = array(
"service" => "coding",
"data" => array(
"ITEM" => array(
array(
"CODE" => "9999", //STANDARD
"QUANTITY" => 1
),
)
)
);
$array2 = array(
array(
"a" => "1",
"b" => "1",
"c" => "1",
"d" => "1",
),
array(
"cancel" => "1",
"a" => "1",
"b" => "",
"c" => "",
"d" => "1",
),
array(
"cancel" => "1",
"a" => "",
"b" => "1",
"c" => "1",
"d" => "",
),
);
for ($i = 0; $i < count($array2); $i++) {
foreach ($array2[$i] as $key => $value) {
if($key == 'a' && $value == 1){
array_push($array1['data']['ITEM'],
array('SOMETHING' => 'this_is_a',
'ELSE' => "1"
)
);
}
if($key == 'c' && $value == 1){
array_push($array1['data']['ITEM'],
array('SOMETHING' => 'this_is_c',
'ELSE' => "1"
)
);
}
}
echo "Loop #$i result:\n";
var_export($array1);
echo "\n";
}
You can test the above code as a PHP Sandbox snippet.
The actual result is:
Loop #0 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
),
),
)
Loop #1 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
3 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
),
),
)
Loop #2 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
3 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
4 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
),
),
)
The loop #0 result is correct, but the later loops have additional entries in $array1['data']['ITEM']. Desired result:
Loop #0 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_a'
'ELSE' => 1
)
2 => array (
'SOMETHING' => 'this_is_c'
'ELSE' => 1
)
)
)
)
Loop #1 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_a'
'ELSE' => 1
)
)
)
)
Loop #2 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_c'
'ELSE' => 1
)
)
)
)
You may use array_map to loop through the second array and only push to the first array's ['data']['ITEM'] when the current iteration has a key named a and its value is 1.
$arr1 = [
'service' => 'coding',
'data' => [
'ITEM' => [
[
'CODE' => '9999',
'QUANTITY' => 1
]
]
]
];
$arr2 = [
[
'a' => '1',
'b' => '1',
'c' => '1',
'd' => '1',
],
[
'cancel' => '1',
'a' => '1',
'b' => '',
'c' => '',
'd' => '1',
],
[
'cancel' => '1',
'a' => '',
'b' => '1',
'c' => '1',
'd' => '',
],
];
// loop through the second array ($arr2)
// the "use" is very important here as it let's the callback function to access $arr1 variable
$finalArr = array_map(function ($el) use ($arr1) {
// if in the current iteration a kley named "a" and its value is "1" is found then push it to the first array's ['data']['ITEM'] key.
// $arr1 here is passed by value so the real array won't be channged, we're, more or less, working with copy of $arr1 in the function.
isset($el['a']) && $el['a'] == 1 && ($arr1['data']['ITEM'][] = [
'something' => 'This is a', // tried to keep same output as yours
'else' => $el['a'] // tried to keep same output as yours
]);
// for "c" key
isset($el['c']) && $el['c'] == 1 && ($arr1['data']['ITEM'][] = [
'something' => 'This is c', // tried to keep same output as yours
'else' => $el['c'] // tried to keep same output as yours
]);
// return the $arr1 copy regardless of whether we pushed "a" key or not.
return $arr1;
}, $arr2);
// print the resulting array
print_r($finalArr);
Result (for the sample data):
array(
0 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is a',
'else' => '1',
),
2 => array(
'something' => 'This is c',
'else' => '1',
),
),
),
),
1 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is a',
'else' => '1',
),
),
),
),
2 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is c',
'else' => '1',
),
),
),
),
)
You can learn more about callback functions (anonymous functions) in the PHP manual.
You are not returning $array1['data']['ITEM'] to its original state between each iteration. Just write the following between your for() and your foreach().
$array1['data']['ITEM'] = array_slice($array1['data']['ITEM'], 0, 1);
Because array keys must be unique, you don't need to loop through all elements to see if a or c exists -- this will cost unnecessary cycles.
Futhermore, I find array destructuring to be a good choice to isolate the only two values you seek.
I might recommend this much simpler and more intuitive snippet: (Demo)
foreach ($array2 as $i => ['a' => $a, 'c' => $c]) {
$temp = $array1;
if($a == 1) {
$temp['data']['ITEM'][] = [
'SOMETHING' => 'this_is_a',
'ELSE' => '1'
];
}
if ($c == 1) {
$temp['data']['ITEM'][] = [
'SOMETHING' => 'this_is_c',
'ELSE' => '1'
];
}
echo "Loop #$i result:\n";
var_export($temp);
echo "\n";
}
The unexpected entries are present because $array1 is modified with each iteration (specifically, by array_push). To prevent this, each iteration must operate on a different copy of $array1. Since variable assignment and argument passing will each copy an array, either could be used. Similar to argument passing, closures can inherit (note: not in the OOP sense) variables from outer scopes, binding to their values (which will copy an array), providing a third potential basis for a solution.
The fix requiring the least amount of edits is to assign $array1 to another variable at the start of the loop, and replace $array1 in the rest of the loop with this variable:
for ($i = 0; $i < count($array2); $i++) {
$result = $array1;
// ...
Alternatively, by moving the body of the loop to a function, $array1 becomes a copy within the function body:
function process(array $options, array $output) {
// Ensure entries to test are present:
$options += ['a' => NULL, 'c' => NULL];
/* As an alternative to the above, if entries should be added
* to $output whenever 'a' or 'c' has a truthy value,
* `! empty(...)` could be used instead of the `... == 1`
* tests below.
*/
if ($options['a'] == 1) {
$output['data']['ITEM'][] = [
'SOMETHING' => 'this_is_a',
'ELSE' => 1,
];
}
if ($options['c'] == 1) {
$output['data']['ITEM'][] = [
'SOMETHING' => 'this_is_c',
'ELSE' => 1,
];
}
return $output;
}
foreach ($array2 as $i => $subarray) {
echo "// Loop #$i result:";
var_export( process($subarray, $array1) );
echo ";\n";
}
As a third approach, you could apply array_map to $array2, as is done by ths. For comparison, the preceding foreach loop could be replaced with a call to array_map:
var_export(
array_map(function (array $subarray) use ($array1) {
return process($subarray, $array1);
}, $array2)
);
Or, with an arrow function (which automatically use variables from the outer scope):
var_export(
array_map(
fn (array $subarray) => process($subarray, $array1),
$array2
) );
Note that for arrays that won't be modified (or for which modification won't matter), you can pass them by reference (or bind by reference in closures) to avoid the overhead of copying them.
function process(array &$options, array $output) {
// ...
I have this associative array
$data = array(
0=>array(
'id'=>1,
'cust_id'=>51,
'roomtype'=>'PREMIUM',
'start'=>'2018-12-20',
'end'=>'2018-12-25',
),
1=>array(
'id'=>2,
'cust_id'=>51,
'roomtype'=>'PRESIDENTIAL',
'start'=>'2018-12-26',
'end'=>'2019-01-01'
),
2=>array(
'id'=>3,
'cust_id'=>52,
'roomtype'=>'PREMIUM',
'start'=>'2019-01-08',
'end'=>'2019-'01-12'
)
3=>array(
'id'=>4,
'cust_id'=>52,
'roomtype'=>'DELUXE',
'start'=>'2019-01-13',
'end'=>'2019-'01-20'
),
4=>array(
'id'=>5,
'cust_id'=>53,
'roomtype'=>'DOUBLE',
'start'=>'2019-01-13',
'end'=>'2019-'01-25'
)
)
I wanted to get the number of times this cust_id had booked, and I wanted to add it in my other array, Im having a hard time as to how am I gonna get the number of iteration per customer based on the cust_id
My desired output:
$new = array(
0=>array(
'id'=>1,
'cust_id'=>51,
'roomtype'=>'PREMIUM',
'start'=>'2018-12-20',
'end'=>'2018-12-25',
'iteration'=>1
),
1=>array(
'id'=>2,
'cust_id'=>51,
'roomtype'=>'PRESIDENTIAL',
'start'=>'2018-12-26',
'end'=>'2019-01-01',
'iteration'=>2
),
2=>array(
'id'=>3,
'cust_id'=>52,
'roomtype'=>'PREMIUM',
'start'=>'2019-01-08',
'end'=>'2019-'01-12',
'iteration'=>1
)
3=>array(
'id'=>4,
'cust_id'=>52,
'roomtype'=>'DELUXE',
'start'=>'2019-01-13',
'end'=>'2019-'01-20',
'iteration'=>2
),
4=>array(
'id'=>5,
'cust_id'=>53,
'roomtype'=>'DOUBLE',
'start'=>'2019-01-13',
'end'=>'2019-'01-25',
'iteration'=>1
)
)
My sample code:
$i=1;
$new = array();
foreach ($data as $key=>$value) {
if ($value['cust_id'] == $value['cust_id']) {
$new[$key]['iteration']
$new[$key] = $value;
$i++;
}
}
Try this:
$usedIdsArr = [];
foreach ($data as $key => $row) {
if (!array_key_exists($row['cust_id'], $usedIdsArr)) {
$usedIdsArr[$row['cust_id']] = 1;
} else {
$usedIdsArr[$row['cust_id']]++;
}
$data[$key]['iteration'] = $usedIdsArr[$row['cust_id']];
}
I'm tracking all the ids and how many times they're used in $usedIdsArr. Each iteration I check if the id is in $usedIdsArr, if not, I add it with a value of one. If it is in $usedIdsArr, I increment the value. Then I add the key of 'iteration' to $data with the value I got from $usedIdsArr.
3v4l.org demo
I find this a much more efficient array.
array (
51 =>
array (
0 =>
array (
'id' => 1,
'cust_id' => 51,
'roomtype' => 'PREMIUM',
'start' => '2018-12-20',
'end' => '2018-12-25',
),
1 =>
array (
'id' => 2,
'cust_id' => 51,
'roomtype' => 'PRESIDENTIAL',
'start' => '2018-12-26',
'end' => '2019-01-01',
),
),
52 =>
array (
0 =>
array (
'id' => 3,
'cust_id' => 52,
'roomtype' => 'PREMIUM',
'start' => '2019-01-08',
'end' => '2019-01-12',
),
1 =>
array (
'id' => 4,
'cust_id' => 52,
'roomtype' => 'DELUXE',
'start' => '2019-01-13',
'end' => '2019-01-20',
),
),
53 =>
array (
0 =>
array (
'id' => 5,
'cust_id' => 53,
'roomtype' => 'DOUBLE',
'start' => '2019-01-13',
'end' => '2019-01-25',
),
),
)
https://3v4l.org/dpl2C
it's multidimensional and the key is the customer id. Inside each subarray you have all the bookings and can easily count on each customer.
In your array you need to find the maximum value of each customer id.
I can just echo count($new[52]); to get the number of bookings for "52".
You can get that from this code:
foreach($data as $sub){
$new[$sub['cust_id']][]= $sub;
}
var_export($new);
I have an array that looks something like below, but it could potentially contain more drafts.
$multi_arr = array(
'draft1' => array (
'alloc_coeff' => 560,
'steps_count' => 2,
'order_draft' => array(
'0' => array(
'whse_id' => 4,
'quantity' => 0,
'percent' => 0
),
'1' => array(
'whse_id' => 1,
'quantity' => 10,
'percent' => 66.666666666667
)
)
),
'draft2' => array (
'alloc_coeff' => 1517,
'steps_count' => 1,
'order_draft' => array(
'0' => array(
'whse_id' => 1,
'quantity' => 10,
'percent' => 66.666666666667
)
)
),
'draft3' => array (
'alloc_coeff' => 559,
'steps_count' => 2,
'order_draft' => array(
'0' => array(
'whse_id' => 2,
'quantity' => 0,
'percent' => 0
),
'1' => array(
'whse_id' => 1,
'quantity' => 10,
'percent' => 66.666666666667
)
)
)
);
I need to sort the content by using two variables: alloc_coeff and steps_count.
First alloc_coeff needs to be considered from the highest to the lowest value.
And then as a second variable the steps_count from lowest value to highest.
usort($multi_arr, function($a, $b) {
return $a['alloc_coeff'] <=> $b['alloc_coeff'];
});
I don't need the entire array to be rewritten and stored in a temporary variable, I just need the keys sorted like this (expected outcome) draft2, draft1 and lastly draft3.
What's a way to accomplish this?
You can do it like below:-
array_multisort(array_column($multi_arr, 'alloc_coeff'), SORT_DESC,
array_column($multi_arr, 'steps_count'), SORT_ASC,
$multi_arr);
Output:- https://eval.in/924894 And https://eval.in/924892
Note:- tested in 5.6.23 and PHP 7.0.8
I trying to return data every time inside the foreach() but i keep on getting the data on the first iteration of the loop here is my code
for ($i = 0 ;$i<4;$i++)
{
var_dump($i);
$publisher = Publisher::find($results[$i]->publisherId);
//pr($publisher);
$channels =$publisher->channels()->get() ;
pr($channels[0]);
$data = ReviveAgent::getPublisherDailyStatistics($channels[0],$start,$end);
pr($data);
return Response::json($data);
}
on the var_dump($i);
It only shows data for the first 0 only.So how can i return the data also for 1,2,3
here is my output when pr($data) for var_dump($i) = 1
array (
0 =>
array (
'impressions' => 1867,
'clicks' => 14,
'requests' => 44,
'revenue' => 2.79,
'day' =>
stdClass::__set_state(array(
'scalar' => '20150518T00:00:00',
'timestamp' => 1431907200,
'xmlrpc_type' => 'datetime',
)),
),
1 =>
array (
'impressions' => 2197,
'clicks' => 17,
'requests' => 382,
'revenue' => 19.829999999999998,
'day' =>
stdClass::__set_state(array(
'scalar' => '20150519T00:00:00',
'timestamp' => 1431993600,
'xmlrpc_type' => 'datetime',
)),
),
2 =>
array (
'impressions' => 5484,
'clicks' => 3,
'requests' => 3680,
'revenue' => 6.7300000000000004,
'day' =>
stdClass::__set_state(array(
'scalar' => '20150520T00:00:00',
'timestamp' => 1432080000,
'xmlrpc_type' => 'datetime',
)),
),
3 =>
array (
'impressions' => 6909,
'clicks' => 105,
'requests' => 5141,
'revenue' => 378.88499999999999,
'day' =>
stdClass::__set_state(array(
'scalar' => '20150521T00:00:00',
'timestamp' => 1432166400,
'xmlrpc_type' => 'datetime',
)),
),
The return operator implicitly ends the current execution scope. The context of your usage is not given, but you could put your $data into an array prior to JSON encoding and returning it. It might look something like this:
$data = array();
for ($i = 0; $i < 4; $i++) {
...
$record = ReviveAgent::getPublisherDailyStatistics($channels[0], $start, $end));
pr($record);
$data[] = $record;
}
return Response::json($data);
You can take this example in laravel
Here I am returning multiple Prices
$prices = $this->Prices;
foreach ($prices as $price) {
$res[] = [
'travel_mode_id' =>$price->travel_mode_id,
'occupancy_id' => $price->occupancy_id,
'rider_id' => $price->occupancy_id,
'price' => $price->price,
];
}
return $res;
I have this Array:
$mergedItems = array(
0 => array(
'id_item' => 'AZ-110'
'amount' => 12
),
1 => array(
'id_item' => 'BZ-110',
'amount' => 13
),
2 => array(
'id_item' => 'BZ-210',
'amount' => 28
),
3 => array(
'id_item' => 'CZ-291',
'amount' => 11
)
);
AND this Array:
$items = array(
0 => array(
'number' => 'AZ-110'
),
1 => array(
'number' => 'BZ-110'
),
2 => array(
'number' => 'CZ-291'
),
3 => array(
'number' => 'BZ-210'
)
);
Now what i want is to order the first array by the id_item Value to match the same order than the 2nd one by its values.
The resulting array has to include all values of the 2nd array AND the belonging amount-value of the first array. The Keys must not be kept!
I can't use array_merge since the 2nd Array has a dynamic amount of more items, so i only want all items from the second Array that are set in the first one.
Does anyone get what i mean? I am searching for a quick and non-dirty way to get this result as expected.
/Edit:
Expected Array:
$detailedItems = array(
0 => array(
'number' => 'AZ-110',
'amount' => 12
),
1 => array(
'number' => 'BZ-110',
'amount' => 13
),
2 => array(
'number' => 'CZ-291',
'amount' => 11
),
3 => array(
'number' => 'BZ-210',
'amount' => 28
)
);
A PHP 5.5 solution:
$itemMap = array_flip(array_column($mergedItems, 'id_item'));
$result = array_map(
function($i) use($itemMap, $mergedItems) {
return $mergedItems[$itemMap[$i['number']]];
},
$items);
print_r($result);
For 5.3 <= PHP < 5.5 you can simply substitute array_map for array_column:
$itemMap = array_flip(array_map(
function($i) { return $i['id_item']; },
$mergedItems));
How it works
The idea is to create a map of item numbers to indexes inside $mergedItems, ie.
[
'AZ-100' => 0,
'BZ-110' => 1,
'BZ-210' => 2,
// etc
]
With this information at hand it's very easy to iterate over $items (so that the result will be ordered based on that array) and pick the appropriate element from $mergedItems to append to the result each time.
$temp = $items;
foreach($temp as &$val)
{
foreach($mergedItems as $item)
{
if($item['id_item'] == $val['number'])
{
$val['amount'] = $item['amount'];
break;
}
}
}
print_r($temp);
There isn't really a "non-dirty" (meaning single line) way to do this as far as I know, but this function should work:
$out = array();
foreach ($mergedItems as $key => $value) {
if (array_key_exists($key, $detailedItems)) { // Make sure it exists to prevent errors
$out[$key] = $detailedItems[$key] + array('amount' => $value['amount']);
} else {
$out[$key] = $value['amount'];
}
}
print_r($out);
You can try following codes:
foreach ($mergedItems as $item) {
$merged[$item['id_item']] = array('amount' => $item['amount']);
}
foreach ($items as $item)
{
$detailedItems[] = array_merge($item, $merged[$item['number']]);
}
Output
var_dump($detailedItems);
array (size=4)
0 =>
array (size=2)
'number' => string 'AZ-110' (length=6)
'amount' => int 12
1 =>
array (size=2)
'number' => string 'BZ-110' (length=6)
'amount' => int 13
2 =>
array (size=2)
'number' => string 'CZ-291' (length=6)
'amount' => int 11
3 =>
array (size=2)
'number' => string 'BZ-210' (length=6)
'amount' => int 28