Multidimensional Array - Match by odd key - php

I am trying to find the difference of 2 multidimensional arrays. I am attempting to solve this with a modified recursive array difference function.
If I have the following array setup:
$array1 = array(
0 => array(
'Age' => '1004',
'Name' => 'Jack'
),
1 => array (
'Age' => '1005',
'Name' => 'John'
)
);
$array2 = array(
0 => array(
'Age_In_Days' => '1004',
'Name' => 'Jack'
),
1=> array(
'Transaction_Reference' => '1005',
'Name' => 'Jack'
)
);
I am trying to match the arrays however the keys are not the same. I want to return the difference between the two multidimensional arrays where
$array1[$i]['Age'] == $array2[$i]['Age_In_Days'];
I want to keep the original array structure if the above condition holds true so the output I am looking for is:
$diff = array (1 => array (
'Age' => '1005',
'Name' => 'John'
));
However I am having issues with how to modify the recursive function to achieve this. Any help is appreciated! Thanks!

You need to loop through first array and compare values with second array. Then follows your condition. If condition is true then push this unique value to third array. Values in third array are now diff between first and second array.
$diff = [];
foreach ($array1 as $value1) {
foreach ($array2 as $value2) {
if ($value1['Age'] !== $value2['Age_In_Days']) {
array_push($diff, $value1);
}
}
}

Related

Getting multiple specific key value pairs if exist in associate array

I want to convert my Associate Array into array based on certain criteria:
It should contain multiple column (Hence, can't user array_column in this case.)
It should ADD duplicate date
It should remove particular sub-array if NULL. (array_filter)
Input :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
'b' => '1',
),
1 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '1',
'b' => '1',
),
2 =>
array (
'date' => "2019-04-02", // If "SAME" date then ADD them
'a' => '2',
'b' => '1',
),
3 =>
array (
'date' => "2019-04-10",
'a' => '', // If "NULL" then remove the particular sub-array
'b' => '1',
),
4 =>
array (
'date' => "2019-04-18",
'a' => '4',
'b' => '10',
),
)
I've tried the following,
Although I was able to select multiple column in array but this is giving me:
"Duplicate" date.
And including sub-array which has "Null" value.
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
return array_map(function ($el) use ($keys) {
$o = [];
foreach($keys as $key){
$o[$key] = isset($el[$key])?$el[$key]:false;
}
return $o;
}, $array);
}
$result = colsFromArray($arr, array("date", "a"));
Required Output :
array (
0 =>
array (
'date' => "2019-03-31",
'a' => '1',
),
1 =>
array (
'date' => "2019-04-02",
'a' => '3',
),
2 =>
array (
'date' => "2019-04-18",
'a' => '4',
),
)
You could try something like this:
function colsFromArray(array $array, $keys)
{
if (!is_array($keys)) $keys = [$keys];
$returnArray = [];
foreach($array as $in){
foreach($keys as $key){
//Check if the key already has a value
if(isset($returnArray[$in['date']][$key]) && is_numeric($returnArray[$in['date']][$key])){
$returnArray[$in['date']][$key] += $in[$key];
}
//If the key is empty, continue
elseif(empty($in[$key])){
continue;
}
//If value is to be set, set date as well
else{
$returnArray[$in['date']]['date'] = $in['date'];
$returnArray[$in['date']][$key] = $in[$key];
}
}
}
//Return array after resetting indexes and removing empty entries
return array_values(array_filter($returnArray));
}
The way that you are passing in the array of columns to your customer function is ambiguous about what should be used to group by and what should be summed. OR if you will never have more than two elements in your 2nd parameter, then your custom function lacks flexibility.
Notice how #SverokAdmin's answer has date hardcoded in the logic, but there is no guarantee that this column name will always exist in the first parameter $array. Ergo, the utility function cannot be reliably used as a utility function.
Consider this custom function which provides your desired result, but allows more meaningful parameter declarations. My snippet allows you to group by one or more column, keep one or more columns in the subarrays, and specify which column should be summed. I cannot make the last column iterable because you state that if a column is missing a value, it should be omitted from the result. In other words, your desired result is unknown if you wanted to sum a and b, but one of the a values was empty.
Code: (Demo)
function colsFromArray(array $array, $groupBy, $keepables, $summable): array
{
$groupBy = (array)$groupBy;
$keepables = (array)$keepables;
// cannot make $summable iterable type because question is unclear on the desired outcome when one of the columns is empty
return array_values(
array_reduce(
$array,
function ($result, $row) use($groupBy, $keepables, $summable) {
$uniqueKey = [];
foreach ($groupBy as $col) {
$uniqueKey[] = $row[$col];
}
$uniqueKey = implode('_', $uniqueKey);
if (!$row[$summable]) {
return $result;
}
foreach ($keepables as $keepable) {
$result[$uniqueKey][$keepable] = $row[$keepable];
}
$result[$uniqueKey][$summable] = ($result[$uniqueKey][$summable] ?? 0) + $row[$summable];
return $result;
},
[]
)
);
}
var_export(
colsFromArray($arr, 'date', 'date', 'a')
);
Output:
array (
0 =>
array (
'date' => '2019-03-31',
'a' => 1,
),
1 =>
array (
'date' => '2019-04-02',
'a' => 3,
),
2 =>
array (
'date' => '2019-04-18',
'a' => 4,
),
)

Filter 2D array using another 2D array where differently keyed column values intersect

I have an array like this:
$array1 = [
[
'Firepack_sn' => '20012205',
'Installation_Date' => '',
'Type' => 'EH',
'Capacity_m3h' => '81',
'Pressure_bar' => '3,4',
'Rpm' => '2930',
'Power_kw' => '72',
],
[
'Firepack_sn' => '20023901',
'Installation_Date' => '',
'Type' => 'DH',
'Capacity_m3h' => '195',
'Pressure_bar' => '4,2',
'Rpm' => '2000',
'Power_kw' => '72',
],
];
And an array2 like this:
$array2 = [
[
'user_id' => '40009',
'firepack_id' => '20012205',
'activated' => '1',
],
[
'user_id' => '40009',
'firepack_id' => '21020393',
'activated' => '0',
],
];
Now I want to filter the first array, so I only get the rows with a Firepack_sn value that exists in array2 as firepack_id.
Desired output:
[
{
"Firepack_sn":"20012205",
"Installation_Date":"",
"Type":"EH","Standard":"VAS",
"Capacity_m3h":"81",
"Pressure_bar":"3,4",
"Rpm":"2930",
"Power_kw":"72"
}
]
How can I achieve this?
Here is a way to do it :
First, you need to extract the firepack_id you need to look for, then you need to loop through $arr1 and check if Firepack_sn is the same than than one of the firepack_id you extracted before, if yes, then you add it to an array.
$arr1 = json_decode('[{"Firepack_sn":"20012205","Installation_Date":"","Type":"EH","Standard":"VAS","Capacity_m3h":"81","Pressure_bar":"3,4","Rpm":"2930","Power_kw":"72","Pump_Type":"KSB KFP50-200","Motor_Type":"DOOSAN PU066 VAS/CEA","Controller_Type":"WB882-E10 VAS","Pump_sn":"085259","Motor_sn":"EARPA209635","Controller_sn":"","Servicelevel":"","Cust_id":"0","Cust_branche":"","Cust_name":"","Cust_address1":"","Cust_zipcode":"","Cust_city":"","Cust_country":"","Cust_Phone":"","Cust_coachlevel":"","Site_Name":"E-set","Site_address1":"","Site_address2":"","Site_address3":"","Site_zipcode":"","Site_city":"","Site_country":"","Site_contact":"","Site_phone":"","activated":"1"},{"Firepack_sn":"20023901","Installation_Date":"","Type":"DH","Standard":"VAS","Capacity_m3h":"195","Pressure_bar":"4,2","Rpm":"2000","Power_kw":"72","Pump_Type":"KSB KFP50-200","Motor_Type":"DOOSAN PU066 VAS/CEA","Controller_Type":"WB882-E10 VAS","Pump_sn":"085259","Motor_sn":"EARPA209635","Controller_sn":"","Servicelevel":"","Cust_id":"0","Cust_branche":"","Cust_name":"","Cust_address1":"","Cust_zipcode":"","Cust_city":"","Cust_country":"","Cust_Phone":"","Cust_coachlevel":"","Site_Name":"D-set","Site_address1":"","Site_address2":"","Site_address3":"","Site_zipcode":"","Site_city":"","Site_country":"","Site_contact":"","Site_phone":"","activated":"0"}]');
$arr2 = json_decode('[{"user_id":"40009","firepack_id":"20012205","activated":"1"},{"user_id":"40009","firepack_id":"21020393","activated":"0"}]');
$firepackIds = array();
foreach($arr2 as $item){
$firepackIds[] = $item->firepack_id;
}
$goodRows = array();
foreach($arr1 as $item){
if(in_array($item->Firepack_sn, $firepackIds)){
$goodRows[] = $item;
}
}
echo json_encode($goodRows);
Hope this helps.
You do not need multiple loops or inefficient iterated in_array() calls. PHP already offers a native function to compare rows between multiple 2D arrays -- array_uintersect(). Because the custom callback uses rows from either array while making comparisons, you must build the callback's logic to fallback to potentially use either array's target column value.
Code: (Demo)
var_export(
array_uintersect(
$array1,
$array2,
fn($a, $b) =>
($a['Firepack_sn'] ?? $a['firepack_id'])
<=>
($b['Firepack_sn'] ?? $b['firepack_id'])
)
);
Output:
array (
0 =>
array (
'Firepack_sn' => '20012205',
'Installation_Date' => '',
'Type' => 'EH',
'Capacity_m3h' => '81',
'Pressure_bar' => '3,4',
'Rpm' => '2930',
'Power_kw' => '72',
),
)

Flatten indexed array

I'm trying to insert a PHP array into a mysql database, but am having trouble with this particular array. I'm trying to create a function that takes
array( 0 => array ( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => value', 'col5' => 'value', 'col6' => array ( 'string' => array ( 'col7' => 'value' , 'col8' => 'value'), ), ),
1 => array ( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => array ( ), 'col5' => 'value', 'col6' => array ( 'string' => array ( ), ), ),
2 => array ( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => array ( ), 'col5' => 'value', 'col6' => array ( 'string' => array ( ), ), ), )
and seperates each numbered array and formats it as:
array( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => 'value', 'col5' => 'value', ['col6' => 'value','col7' => 'value',] )
array( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => 'value', 'col5' => 'value', ['col6' => 'value','col7' => 'value',] )
array( 'col1' => 'value', 'col2' => 'value', 'col3' => 'value', 'col4' => 'value', 'col5' => 'value', ['col6' => 'value','col7' => 'value',] )
depending on how many rows there are. Keep in mind these conditions:
+8 cols is just an example, the range can fluctuate greatly.
+Cols containing a array must be non existent if it empty, like in [1] and [2], but not [0].
+Any column could contain a empty or full array. if it contains a full array, it needs to be flatten while retaining it's value.
+Some arrays might have greater then 2 nested arrays. (elements)
+Not all of the arrays have nested arrays, they are already formatted correctly. These arrays can not be affected by the PHP function i'm trying to create.
I'm stumped, every function I've created has failed. Thanks everyone in advance.
UPDATE I used Var_export with this function to get the array above.
function flatten($array, $preserve_keys = false)
{
if (!$preserve_keys) {
$array = array_values($array);
}
$flattened_array = array();
foreach ($array as $k => $v) {
$flattened_array[$k] = $v;
if (is_array($v)) {
$flattened_array = array_merge($flattened_array, call_user_func(__FUNCTION__, $v, $preserve_keys));
} elseif ($preserve_keys) {
$flattened_array[$k] = $v;
} else {
$flattened_array[] = $v;
}
}
return (array)$flattened_array;
}
What you need is what's called a recursive function (see this question for more info: What is a RECURSIVE Function in PHP?).
Also it seems that each "col" in the array is a string (and not a number).
What you might want to try is check whether the first (or any) key in the array is numeric.
The code would look something like this:
public function extractArray($myArray){
$extractedArray = array(); // create a new array where all the flattened data will go
$arrayKeys = array_keys($myArray); // grab all the keys from the array
if (is_numeric($arrayKeys[0])): // if the first key is a number (it's nested)
$extractedArray = $myArray[0]; // remove one level of the array by grabbing everything from the first element
extractArray($extractedArray); // run our function again to check if we need to remove another layer
else:
$extractedArray = $myArray; // seems like there are no layers that need to be removed
endif;
return $extractedArray;
}
Edit:
Here is a possible solution for the second part of the problem (extracting values from possible sub-arrays)
Here are the steps we are going to take:
1) Loop through each element in our $extractedArray (from part 1) and see if it is a sub array
2) If it is an array we extract it's contents and place it in a temporary variable (an array for now)
3) We then call ourselves again (recursively) passing it the new temporary variable (until there are no more sub-arrrays left
4) Once we have extracted all the values and flattened them in to a single array, we can then loop through that array and store them as a string (or whatever else we like)
Updated Code
Here is the code:
public function flattenArrayValues($extractedArray){
$extractedValues = array();
foreach($extractedArray as $key => $eA):
if(is_array($eA)): // check whether current element is an array
reset($eA); // go to the first element of the array and grab it
$first_key = key($eA); // ...
$extractedValues[$key] = $eA[$first_key]; // this allows us to preserve the "cols" for each element
flattenArrayValues($extractedValues); //call ourselves again to check whether there are any sub-arrays
endif;
endforeach;
return $extractedValues; // seems like we extracted all the values and flattened them
// if we want we can loop through this new array and build a string out of it etc.
}
Hope this was helpful

multidimensional array - adding a key and value where a key and value is matched

I'm trying to add a key and value (associative) from an array to another array, where one specific key and value match. Here are the two arrays:
$array1 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2'
),
2 => array(
'walmart' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'milk' => 'product3'
)
);
$array2 = array(
1 => array(
'walgreens' => 'location',
'apples' => 'product1',
'oranges' => 'product2',
'bananas' => 'product3',
)
);
Here is the attempt I made at modifying $array1 to have key 'bananas' and value 'product3':
$dataCJ = getCJItem($isbn);
foreach ($array1 as $subKey => $subArray) {
foreach($subArray as $dkey => $dval){
foreach($array2 as $cjk => $cjv){
foreach($cjv as $cjkey => $cjval){
if($dval['walgreens'] == $cjval['walgreens']){
$dval['bananas'] = $cjval['bananas'];
}
}
}
}
}
This doesn't work. How can I fix this?
Change => $dval to => &$dval. Currently you are creating and writing to a new variable and the update will not work in-place.
I would look at array_merge() function!
Here is a start with the PHP doc.
For your specific case, you could do the following :
foreach($array1 as $key1 => $values1){
foreach($array2 as $key2 => $values2){
if($values1[0] == $values2[0]){
$array1[$key1] = array_merge($values1, $values2);
}
}
}
Note to simplify the problem you should inverse the first key=> value pair of the array.
Having an array this way would be a lot simper :
array(
'location' => "The location (eg:walgreens)",
//...
);
This way you could change the comparison to the following instead :
$values1['location'] == $values2['location']
Which would be safer in the case the array is not built with the location as the first pair.

Matching an array value by key in PHP

I have an array of items:
array(
[0] => array(
'item_no' => 1
'item_name' => 'foo
)
[1] => array(
'item_no' => 2
'item_name' => 'bar'
)
) etc. etc.
I am getting another array from a third party source and need to remove items that are not in my first array.
array(
[0] => array(
'item_no' => 1
)
[1] => array(
'item_no' => 100
) # should be removed as not in 1st array
How would I search the first array using each item in the second array like (in pseudo code):
if 'item_no' == x is in 1st array continue else remove it from 2nd array.
// Returns the item_no of an element
function get_item_no($arr) { return $arr['item_no']; }
// Arrays of the form "item_no => position in the array"
$myKeys = array_flip(array_map('get_item_no', $myArray));
$theirKeys = array_flip(array_map('get_item_no', $theirArray));
// the part of $theirKeys that has an item_no that's also in $myKeys
$validKeys = array_key_intersect($theirKeys, $myKeys);
// Array of the form "position in the array => item_no"
$validPos = array_flip($validKeys);
// The part of $theirArray that matches the positions in $validPos
$keptData = array_key_intersect($theirArray, $validPos);
// Reindex the remaining values from 0 to count() - 1
return array_values($keptData);
All of this would be easier if, instead of storing the key in the elements, you stored it as the array key (that is, you'd be using arrays of the form "item_no => item_data") :
// That's all there is to it
return array_key_intersect($theirArray, $myArray);
You can also do:
$my_array =array(
0 => array( 'item_no' => 1,'item_name' => 'foo'),
1 => array( 'item_no' => 2,'item_name' => 'bar')
);
$thrid_party_array = array(
0 => array( 'item_no' => 1),
1 => array( 'item_no' => 100),
);
$temp = array(); // create a temp array to hold just the item_no
foreach($my_array as $key => $val) {
$temp[] = $val['item_no'];
}
// now delete those entries which are not in temp array.
foreach($thrid_party_array as $key => $val) {
if(!in_array($val['item_no'],$temp)) {
unset($thrid_party_array[$key]);
}
}
Working link
If your key is not actually a key of your array but a value, you will probably need to do a linear search:
foreach ($itemsToRemove as $itemToRemove) {
foreach ($availableItems as $key => $availableItem) {
if ($itemToRemove['item_no'] === $availableItem['item_no']) {
unset($availableItems[$key]);
}
}
}
It would certainly be easier if item_no is also the key of the array items like:
$availableItems = array(
123 => array(
'item_no' => 123,
'item_name' => 'foo'
),
456 => array(
'item_no' => 456,
'item_name' => 'bar'
)
);
With this you could use a single foreach and delete the items by their keys:
foreach ($itemsToRemove as $itemToRemove) {
unset($availableItems[$itemToRemove['item_no']]);
}
You could use the following to build an mapping of item_no to your actual array keys:
$map = array();
foreach ($availableItems as $key => $availableItem) {
$map[$availableItems['item_no']] = $key;
}
Then you can use the following to use the mapping to delete the corresponding array item:
foreach ($itemsToRemove as $itemToRemove) {
unset($availableItems[$map[$itemToRemove['item_no']]]);
}

Categories