I have some default configurations, and some specific configurations which would be configurable. I need to merge the specific configurations into the default configurations.
In the case that the specific config option does not exist, the
default option will be used.
In the case that the value is a scalar, the specific configuration should be applied
In the case that the value is a scalar array, the arrays should be merged and array_unique applied.
In the case that the value is an associative array, We need to apply the above scalar and scalar_array rules.
Example:
$defaultConfigs = [
'scalar1' => 1,
'scalar2' => "Apple",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [
...
]
],
];
$specificConfigs = [
'scalar1' => "A",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [
...
]
],
];
Expected Output:
$expectedConfigs = [
'scalar1' => "A", // Overridden
'scalar2' => "Apple", // Default used
'array_scalar' => [1,2,3,4,5], // Scalar merged and array_unique
'array_associative' => [
'scalar' => "B", // Overridden
'array_scalar' => [1,2,3,4,5], // Scalar merged and array_unique
'array_associative' => [
...
]
],
];
Is there a nice clean way of achieving this?
This function obtains your desired result. It assume that specific types are coherents with default types, so no coherence check is performed. The function iterate specific configuration array and check corresponding default value1: if it is scalar, replace default value; if it is a enumerated array2, it merge unique values; otherwise function call itself with current values as arguments.
function fillConfig( $default, $specific )
{
foreach( $specific as $key=> $val )
{
if( isset( $default[$key] ) )
{
if( ! is_array( $default[$key] ) )
{
$default[$key] = $val;
}
elseif( array_keys($default[$key]) === range(0, count($default[$key]) - 1) )
{
$default[$key] = array_unique( array_merge( $default[$key], $val ) );
}
else
{
$default[$key] = fillConfig( $default[$key], $val );
}
}
else
{
// This happens when a specific key doesn't exists in default configuration.
// I think that in this case the value must be omitted,
// otherwise you can un-comment following line:
// $default[$key] = $val;
}
}
return $default;
}
Calling the function in this way:
$result = fillConfig( $defaultConfigs, $specificConfigs );
$result, applied to your arrays sample, is this:
Array
(
[scalar1] => A
[scalar2] => Apple
[array_scalar] => Array
(
[0] => 3
[1] => 4
[2] => 5
)
[array_associative] => Array
(
[scalar] => 1
[array_scalar] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[array_associative] => Array
(
)
)
)
With this array couple:
$defaultConfigs = [
'scalar1' => 1,
'scalar2' => "Apple",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => 1,
'array_scalar' => [1,2,3],
'array_associative' => [
]
],
];
$specificConfigs = [
'scalar1' => "A",
'array_scalar' => [3,4,5],
'array_associative' => [
'scalar' => B,
'array_scalar' => [3,4,5],
'array_associative' => [
]
],
];
$result is:
Array
(
[scalar1] => A
[scalar2] => Apple
[array_scalar] => Array
(
[0] => 3
[1] => 4
[2] => 5
)
[array_associative] => Array
(
[scalar] => B
[array_scalar] => Array
(
[0] => 1
[1] => 2
[2] => 3
[4] => 4
[5] => 5
)
[array_associative] => Array
(
)
)
)
Notes:
1 Yes, this is a bit incoherent: I felt was better iterate through specific array (not existent items remain untouched), but performing value check on default array, that is the reference point.
2 The enumerated/associative array check is based on this answer.
My case was slightly different but it might be of help. I needed to replace scalars and array_merge_recursive on arrays.
class ArrayUtil {
public static function mergeRecursive(array $array1, $array2) {
if($array2 && is_array($array2)) {
foreach($array2 as $key => $val2) {
if (is_array($val2) && (null!==($val1 = isset($array1[$key]) ? $array1[$key] : null)) && is_array($val1)) {
$array1[$key] = self::mergeRecursive($val1,$val2);
} else {
$array1[$key] = $val2;
}
}
}
return $array1;
}
}
I rewrote function from the first answer a bit for using with array of configs:
private function mergeConfigs(array $configs): array
{
$default = array_shift($configs);
return array_reduce($configs, function (array $result, array $config) {
foreach ($config as $key => $val) {
if (!isset($result[$key]) || !is_array($result[$key])) {
$result[$key] = $val;
continue;
}
$result[$key] = array_keys($result[$key]) === range(0, count($result[$key]) - 1)
? array_unique(array_merge($result[$key], $val))
: $this->mergeConfigs([$result[$key], $val]);
}
return $result;
}, $default);
}
Related
I've searched stackoverflow and found many similar questions which where solved with array_slice, array_splice and the occasional array_merge, but none of these worked for me and I'm stumped. I must be doing something wrong, but can't figure out what exactly.
My problem:
I have a nested associative array representing a menu structure. It looks like this
$this->navigation_array=[
"CATEGORY_MAIN"=>[
"Page1"=>"page1.php",
"Page2"=>"page2.php",
"Page3"=>"page3.php",
"Page4"=>"page4.php"
],
"CATEGORY_NEW"=>[
"Page1"=>"page1_edit.php",
"Page2"=>"page2_edit.php",
"Page3"=>"page3_edit.php"
],
"CATEGORY_EMPTY1"=>[],
"CATEGORY_EMPTY2"=>[],
"SEARCH"=>[
"Page1"=>"page1_search.php",
"Page2"=>"page2_search.php",
"Page3"=>"page3_search.php"
],
"BACK"=>["Home"=>"index.php"]
];
Now I need a function to add a new category at a given position from either the beginning or the end of the navigation_array. So, either $MENU->category_add("test",2) or $MENU->category_add("test",-1)
What I tried so far fails, even though I tried several different approaches from here, here or here. The current, non-functional, iteration looks like this
public function category_add(string $category,int $position=0): bool{
if(array_key_exists($category,$this->navigation_array))return false;
if($position!=0){
$category_array=array($category=>[]);
array_splice($this->navigation_array,$position,0,$category_array);
}else $this->navigation_array[$category]=array(); //simply append if pos is 0
return true;
}
It does insert at the correct position when I call it with $MENU->category_add("test",2) or $MENU->category_add("test",-1) but what is inserted is [0] => Array ( ) instead of ["test"] => Array ( ).
The resulting navigation array thus looks like this:
Array ( [CATEGORY_MAIN] => Array ( [Page1] => page1.php [Page2] => page2.php [Page3] => page3.php [Page4] => page4.php ) [CATEGORY_NEW] => Array ( [Page1] => page1_edit.php [Page2] => page2_edit.php [Page3] => page3_edit.php ) [CATEGORY_EMPTY1] => Array ( ) [CATEGORY_EMPTY2] => Array ( ) [SEARCH] => Array ( [Page1] => page1_search.php [Page2] => page2_search.php [Page3] => page3_search.php ) [0] => Array ( ) [BACK] => Array ( [Home] => index.php ) )
But should look like
Array ( [CATEGORY_MAIN] => Array ( [Page1] => page1.php [Page2] => page2.php [Page3] => page3.php [Page4] => page4.php ) [CATEGORY_NEW] => Array ( [Page1] => page1_edit.php [Page2] => page2_edit.php [Page3] => page3_edit.php ) [CATEGORY_EMPTY1] => Array ( ) [CATEGORY_EMPTY2] => Array ( ) [SEARCH] => Array ( [Page1] => page1_search.php [Page2] => page2_search.php [Page3] => page3_search.php ) [test] => Array ( ) [BACK] => Array ( [Home] => index.php ) )
I know it's probably something pretty minor or silly that I'm overlooking, but I've stared at this so long, I'm obviously blind to it.
Could somebody be so kind and give me a hint?
Edit
So according to the php doc, ""Note: Keys in the replacement array are not preserved." array_splice does not work. But there must be another way to somehow achieve this, right?
Try this code, it has two functions category_add which is calling arrayInsert custom function, we can add a whole new category as well as insert new page inside of existing category.
<?php
$navigation_array = [
"CATEGORY_MAIN" => [
"Page1" => "page1.php",
"Page2" => "page2.php",
"Page3" => "page3.php",
"Page4" => "page4.php"
],
"CATEGORY_NEW" => [
"Page1" => "page1_edit.php",
"Page2" => "page2_edit.php",
"Page3" => "page3_edit.php"
],
"CATEGORY_EMPTY1" => [],
"CATEGORY_EMPTY2" => [],
"SEARCH" => [
"Page1" => "page1_search.php",
"Page2" => "page2_search.php",
"Page3" => "page3_search.php"
],
"BACK" => [
"Home" => "index.php"
]
];
function category_add(array $array, string $category, array $newElement, int $position = 0)
{
if (array_key_exists($category, $array)) {
$categoryArray = $array[$category];
$categoryArray = arrayInsert($categoryArray, $position, $newElement);
$array[$category] = $categoryArray;
} else {
$array[$category] = $newElement;
}
return $array;
}
function arrayInsert($array, $position, $insertArray)
{
$ret = [];
if ($position == count($array)) {
$ret = $array + $insertArray;
} else {
$i = 0;
foreach ($array as $key => $value) {
if ($position == $i++) {
$ret += $insertArray;
}
$ret[$key] = $value;
}
}
return $ret;
}
$newNavigation = category_add($navigation_array, 'CATEGORY_MAIN', ['Page2aaa' => "page2aaa.php"], 1);
echo '<pre>'; print_r($newNavigation);
// create whole new category
$newNavigation2 = category_add($navigation_array, 'CATEGORY_NEW2', ['new' => "new.php"]);
echo '<pre>'; print_r($newNavigation2);
https://www.php.net/manual/en/function.array-splice.php#111204
When trying to splice an associative array into another, array_splice is missing two key ingredients:
a string key for identifying the offset
the ability to preserve keys in the replacement array
This is primarily useful when you want to replace an item in an array with another item, but want to maintain the ordering of the array without rebuilding the array one entry at a time.
<?php
function array_splice_assoc(&$input, $offset, $length, $replacement) {
$replacement = (array) $replacement;
$key_indices = array_flip(array_keys($input));
if (isset($input[$offset]) && is_string($offset)) {
$offset = $key_indices[$offset];
}
if (isset($input[$length]) && is_string($length)) {
$length = $key_indices[$length] - $offset;
}
$input = array_slice($input, 0, $offset, TRUE)
+ $replacement
+ array_slice($input, $offset + $length, NULL, TRUE);
}
$fruit = array(
'orange' => 'orange',
'lemon' => 'yellow',
'lime' => 'green',
'grape' => 'purple',
'cherry' => 'red',
);
// Replace lemon and lime with apple
array_splice_assoc($fruit, 'lemon', 'grape', array('apple' => 'red'));
// Replace cherry with strawberry
array_splice_assoc($fruit, 'cherry', 1, array('strawberry' => 'red'));
?>
I need to merge arrays into 1 array but what I need is to remove before the main data they b oth have in common (duplicated values i mean), I need only unique values when merged.
array formed from foreach.
public $arrayfields= [];
public $itemMap = [];
public function getRowMapping(array $row,$uniqueField,$moduleName)
{
$arrayData = [];
foreach ($this->columnMapping as $key => $value) {
$row = $this->moduleType($key,$value,$row,$moduleName);
}
$unique = $row[$uniqueField];
if(!isset($this->itemMap[$unique])){
$this->itemMap[$unique] = $row;
$this->itemMap[$unique][$unique]['items'] = [];
}
$this->itemMap[$unique]['items'][] = $row['items'];
return $row;
}
i changed little bit to my input
How can i do that?
so now the input will be like this
Array
(
[bill_type] => 1
[bill_number] => BIL-003
[order_number] => ON-003
[items] => Array
(
[track_inventory] => 0
[sku1] => sku1
[name] => Kidswear1
)
)
Array
(
[bill_type] => 1
[bill_number] => BIL-003
[order_number] => ON-003
[items] => Array
(
[track_inventory] => 0
[sku1] => sku2
[name] => Pant
)
)
Array
(
[bill_type] => 1
[bill_number] => BIL-002
[order_number] => ON-002
[items] => Array
(
[track_inventory] => 0
[sku1] => sku3
[name] => Pants
)
)
The final output I'm looking for is
Array
(
[BIL-003] => Array
(
[bill_type] => 1
[order_number] => ON-003
[items] => Array
(
[0] => Array(
[track_inventory] => 0
[sku1] => sku1
[name] => Kidswear1
)
[1] => Array
(
[track_inventory] => 0
[sku1] => sku2
[name] => Pant
)
)
[BIL-002] => Array
(
[bill_type] => 1
[order_number] => ON-002
[items] => Array
(
[0] => Array(
[track_inventory] => 0
[sku1] => sku3
[name] => pants
)
)
)~~~
I think there is no standard library function to accomplish this.
So here is the function that accomplishes this:
function merge($array1, $array2)
{
$arraymerged = [];
if (is_array($array1) && is_array($array2)) {
//collect the keys of two arrays
$keys = [];
foreach ($array1 as $key => $value) {
$keys[$key] = true;
}
foreach ($array2 as $key => $value) {
$keys[$key] = true;
}
//merge key values for each key
foreach ($keys as $key => $value) {
if (isset($array1[$key]) && isset($array2[$key])) {
if ($array1[$key] == $array2[$key]) {
$arraymerged[$key] = $array1[$key];
} else {
$arraymerged[$key] = [$array1[$key], $array2[$key]];
}
} else if (isset($array1[$key]))
$arraymerged[$key] = $array1[$key];
else
$arraymerged[$key] = $array2[$key];
}
return $arraymerged;
}
}
Here is my suggestion,
if the input is array, check my answer on this post Convert an associative array into an associative array which has a key with another associative array as its value in php
otherwise check below code.
bill_number as unique key and run foreach and append items array elements.
let me give you an example
$arr1 = [
"bill_type" => 1,
"bill_number" => 'BIL-003',
"items"=>[
0 => [
"name"=>"test"
]
]
];
$arr2 = [
"bill_type" => 1,
"bill_number" => 'BIL-003',
"items"=>[
0 => [
"name"=>"test_2"
]
]
];
$formattedArr = [];
//merge these arrays
$combinedArr = [];
$combinedArr[] = $arr1;
$combinedArr[] = $arr2;
foreach($combinedArr as $key=>$value){
$formattedArr[$value['bill_number']]["bill_type"] = $value["bill_type"];
$formattedArr[$value['bill_number']]["items"][] = $value["items"];
}
print_r($formattedArr);
I haven't tested this code, but i think this will be ans to your question.
<?php
$a1 = array(
"bill_type" => "1",
"bill_number" => "BIL-003",
"order_number" => "ON-003",
"items" => array(
"track_inventory" => "0",
"sku1" => "sku1",
"name" => "Kidswear1"
)
);
$a2 = array(
"bill_type" => "1",
"bill_number" => "BIL-003",
"order_number" => "ON-003",
"items" => array(
"track_inventory" => "0",
"sku1" => "sku2",
"name" => "Pant"
)
);
$result = function ($a1, $a2) {
$b1 = $a1['items'];
$b2 = $a2['items'];
$c1 = $a1;
$c2 = $a2;
unset($c1['items']);
unset($c2['items']);
if (count(array_diff($c1, $c2)) == 0) {
if (count(array_diff($b1, $b2)) != 0) {
$c1['items'] = [$b1, $b2];
} else {
$c1['items'] = $b1;
}
return $c1;
}else{
//you should complete this part by yourself, if first parts are not equal.
}
return $c1;
};
$r = $result($a1, $a2);
var_dump($r);
result:
(array) [4 elements]
bill_type: (string) "1"
bill_number: (string) "BIL-003"
order_number: (string) "ON-003"
items:
(array) [2 elements]
0:
(array) [3 elements]
track_inventory: (string) "0"
sku1: (string) "sku1"
name: (string) "Kidswear1"
1:
(array) [3 elements]
track_inventory: (string) "0"
sku1: (string) "sku2"
name: (string) "Pant"
I have an array with user id and transaction details sorted based on transaction. Now I need to find the position of key value
Array
(
[0] => Array
(
[userid] => 3
[transaction] => 1878
)
[1] => Array
(
[userid] => 2
[transaction] => 416
)
[2] => Array
(
[userid] => 5
[transaction] => 353
)
[3] => Array
(
[userid] => 4
[transaction] => 183
)
)
When I give user id 4 then it should return 3
First, use array_column() to fetch all userid, then use array_search() to retrieve array key.
$searchId = 4;
echo array_search( $searchId, array_column($array, 'userid') ) );
I might just iterate the outer array here and the check the key values:
$pos = -1;
for ($i = 0; $i < count($array); $i++) {
if ($array[$i]['userid'] == 4) {
$pos = $i;
}
}
if ($pos == -1) {
echo "user not found";
}
else {
echo "user found at position " . $pos;
}
I prefer Calos's answer.
The code below imitates JavaScript's Array.prototype.findIndex to achieve what you need. Using this approach you can have more flexibility in searching the array by using a callback function, similar to how JavaScript has done it.
You can also easily reuse it in other parts of your code.
$data = [
[
'userid' => 3,
'transaction' => 1878
],
[
'userid' => 2,
'transaction' => 416
],
[
'userid' => 5,
'transaction' => 353
],
[
'userid' => 4,
'transaction' => 183
]
];
function findIndex($array, $method){
foreach($array as $index => $value){
if ($method($value, $index, $array) === true){
return $index;
}
}
}
echo findIndex($data, function($arr){
return $arr['userid'] == 5;
});
It's trivial to build your own map:
<?php
$items =
[
'foo' =>
[
'id' => 23,
],
'bar' =>
[
'id' => 47
]
];
foreach($items as $k=>$v)
$ids_keys[$v['id']] = $k;
echo $ids_keys[47];
Output:
bar
I have an arrays like this
Array
(
[original_data] => Array
(
[0] => Array
(
[reference_id] => 122
[id] => 121
[reference_name] => Dinesh
)
[1] => Array
(
[reference_id] => 123
[id] => 120
[reference_name] => Dinesh Test
)
)
[edited_data] => Array
(
[0] => Array
(
[reference_id] => 123
[id] => 120
[reference_name] => Dinesh Test2
)
)
)
I want to get the difference between this original_data and edited_data arrays. How to do that? I tried using array_diff. But, It didn't work correctly.
You can get the difference =>
$result = ref_array_diff($requestedData['edited_data'], $requestedData['data']);
print_r($result);
function ref_array_diff($arraya, $arrayb) {
foreach ($arraya as $keya => $valuea) {
if (in_array($valuea, $arrayb)) {
unset($arraya[$keya]);
}
}
return $arraya;
}
If you use array_map on both original data and edited data to get an array of original_data id values and edited_data id values. You can then use array_diff with no problem. Using the resulting list of id's which are in one but not the other you can lookup the original value using the array mapped values index key on the original arrays.
I'm making the assumption here that your id key is a unique identifier for each value in the data arrays. If this isn't the case you'll need to do some nested loops to compare deep values
This should work.
<?php
// your data
$data = array (
'original_data' => array
('0' => array
(
'reference_id' => 122,
'id' => 121,
'reference_name' => 'Balasuresh,'
),
'1' => array
(
'reference_id' => 123,
'id' => 120,
'reference_name' => 'Balasuresh',
),
),
'edited_data' => array
('0' => array
(
'reference_id' => 123,
'id' => 120,
'reference_name' => 'Balasuresh Test',
),
)
) ;
// let's get our reference_ids
$originalData = array() ;
foreach($data['original_data'] as $entry) {
$originalData[$entry['reference_id']] = $entry ;
}
// let's compare our edited data to our original data
$editedData = array() ;
foreach($data['edited_data'] as $editedEntry) {
if (isset($originalData[$editedEntry['reference_id']])) {
// for readability
$compare = $originalData[$editedEntry['reference_id']] ;
foreach($editedEntry as $key=>$value) {
if ($compare[$key] != $value) {
$editedData[$editedEntry['reference_id']][$key] = $value ;
}
}
}
}
$editedData now contains your differences
These are multi-dimensional associative arrays, you can recursively do an array_diff_assoc like this:
you can check this code here https://3v4l.org/ovaQB:
function array_diff_assoc_recursive($array1, $array2)
{
foreach($array1 as $key => $value)
{
if(is_array($value))
{
if(!isset($array2[$key]))
{
$difference[$key] = $value;
}
elseif(!is_array($array2[$key]))
{
$difference[$key] = $value;
}
else
{
$new_diff = array_diff_assoc_recursive($value, $array2[$key]);
if($new_diff != FALSE)
{
$difference[$key] = $new_diff;
}
}
}
elseif(!isset($array2[$key]) || $array2[$key] != $value)
{
$difference[$key] = $value;
}
}
return !isset($difference) ? 0 : $difference;
}
$a=["original_data"=>[
[
'reference_id' => 122,
'id' => 121,
'reference_name' => 'Balasuresh'
],[
'reference_id' => 123,
'id' => 120,
'reference_name' => 'Balasuresh'
]
],
'edited_data' => [[
'reference_id' => 123,
'id' => 120,
'reference_name' => 'Balasuresh Test'
]]
];
print_r(array_diff_assoc_recursive($a['edited_data'], $a['original_data']));
output:
Array
(
[0] => Array
(
[reference_id] => 123
[id] => 120
[reference_name] => Balasuresh Test
)
)
I checked several similar questions like this, this, and this but remain not clear that, can I get a firm value by operations inside a deep nested array, rather than calling function or assigning a variable?
As an example below to insert at the first position of $arr['row'] depending on $s:
$s = true; //or false;
$arr = [
'key1'=>'val1',
'key2'=>'val2',
'row'=>[
function($s){
if($s) return array('x'=>'y',...);
else return null;
},
[
'row2a'=>'val2a',
'row2b'=>'val2b',
],
[
'row3a'=>'val3a',
'row3b'=>'val3b',
],
],
];
// Output:
Array(
...
[row] => Array
(
[0] => Closure Object
(
[parameter] => Array
(
[$s] =>
)
)
[1] => Array
(
[row2a] => val2a
[row2b] => val2b
)
...
got Closure Object not array('x'=>'y',...) in $arr['row'][0]. Or it's no way to get value by operations inside an array, but calling function or passing by variables? Thanks.
If this is what you need you can always try this approach:
$s = 1;
$value = call_user_func(function($s) { return $s; }, $s);
var_dump($value);
And it will produce:
int(1)
Try below code
$s=true;
function abc($flag) {
if ($flag):
$array["x"]="x";
$array["y"]="y";
return $array;
else:
return null;
endif;
}
$arr = [
'key1' => 'val1',
'key2' => 'val2',
'row' => [
$resultset = abc($s),
[
'row2a' => 'val2a',
'row2b' => 'val2b',
],
[
'row3a' => 'val3a',
'row3b' => 'val3b',
],
],
];
print_r($arr);
exit;
output
Array
(
[key1] => val1
[key2] => val2
[row] => Array
(
[0] => Array
(
[x] => x
[y] => y
)
[1] => Array
(
[row2a] => val2a
[row2b] => val2b
)
[2] => Array
(
[row3a] => val3a
[row3b] => val3b
)
)
)