Array Elements inconsistently getting added in PHP with array += array - php

This might be a stupid question, but I am having trouble figuring out why this does not always work. I initialize an array and then pass through a number of if statements that add another element to the array. This seems to work most of the time, but sometimes an element does not get added even though the if statement is true. Here is some code:
$data = array ("command" => "/order/list", "account_id" => Config::get('DEFAULT_ACCOUNT_ID'));
if (isset($post['data-mrn']) && $post['data-mrn'] != '' ) {
$data += array("filter.patientid.equals" => $post['data-mrn']);
}
if (isset($post['data-name']) && $post['data-name'] != '' ) {
$data += array("filter.patient_name.like" => '');
}
if (isset($post['data-accession']) && $post['data-accession'] != '' ) {
$data += array("filter.accession_number.equals" =>$post['data-accession']);
}
if (isset($post['data-description']) && $post['data-description'] != '') {
$data += array("filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like" => 'xxx');
}
if (isset($post['data-modality']) && $post['data-modality'] != '' ) {
$data += array("filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like" => 'xxx');
}
if (isset($post['data-status']) && $post['data-status'] != '') {
$data += array("filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like" => 'xxx');
}
if (isset($post['data-date']) && $post['data-date'] != '' ) {
$post['data-date'] = str_replace("-","",$post['data-date']);
$data += array("filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like" => 'xxx');
print_r($data );
}
If I pass in an mrn, an accession_number and a status and a date, I get:
Array (
[command] => /order/list [account_id] => xxx
[filter.patientid.equals] => xxx
[filter.accession_number.equals] => xxx
[filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like] => xxx
)
and it leaves out the date, even though the print_r($data) is in the if statement for the date,
so the data += there is not adding that condition. There are other combinations where it does not update the $data array even though it satisfies the if condition. Just wondering if there is a better way to do that and why that is happening.

You're using the same key filter.customfield-421250f4-ea28-42b2-b53d-06ba84f16d36.like four times. Associative arrays must have unique keys. You can not do this:
$array = [
'foo' => 'bar1',
'foo' => 'bar2',
'foo' => 'bar3',
];
If you have this:
$array = [
'foo' => 'bar1',
];
And then try to merge an array containing an existing key,
$array += [
'foo' => 'bar2',
];
PHP will throw away the duplicate item(s) because the key(s) already exists.
If you instead do this:
$array = [
'foo' => 'bar1',
];
$array['foo'] = 'bar2';
Then PHP will overwrite the existing key with the new value, which isn't what you want either.
So, if you want to maintain all the values specified, you'll either need to use unique keys:
$array = [
'foo1' => 'bar1',
'foo2' => 'bar2',
'foo3' => 'bar3',
];
Or use an indexed array:
$array = [
'bar1',
'bar2',
'bar3',
];
Or maybe (fancy!) an indexed array inside an associative array:
$array = [
'foo' => [
'bar1',
'bar2',
'bar3',
],
];

Related

PHP Nested Arrays: Imploding all the tree keys of each leaf results in a multidimensional array instead of a 1D associative one

From a nested array, I want to generate the 1D associative array which contains, for each leaf, its ascending keys concatenation.
Summary
Expected results example
1.1. Input
1.2. Output
Actual results example
1.1. Input
1.2. Output
Question
Minimal, Testable Executable Sources
4.1. Explanations
4.2. Sources & Execution
Expected results example
Input
The following nested array:
[
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]
Output
The following 1D associative array (glue character for the concatenation of the keys: _):
[
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty'
]
Actual results example
Input
[
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => ['url_fichier' => 'foo' ],
1 => ['url_fichier' => 'bar']
]
]
]
Output
Array
(
[] => bar
[proposition_en_cours] => Array
(
[fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
)
[proposition_en_cours_fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
[proposition_en_cours_fichiers_0] => foo
[proposition_en_cours_fichiers_0_1] => bar
)
Question
As you can see, the array I get differs in all points from the expected one. I can't figure out why.
Minimal, Testable Executable Sources
Explanations
I initialize an array that must contain all the ascending keys for each leaf: $key_in_db_format = [];.
I iterate on the input array. For each element (leaf or subarray), I pop $key_in_db_format if, and only if, the current depth equals the last depth. If it's an array (i.e.: not a leaf): I add the key to $key_in_db_format. I set a value (the leaf) at the key that is the concatenation of the ascending keys.
Sources & Execution
First, define this array in an empty PHP script of your choice:
$values = [
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => [ 'url_fichier' => 'foo' ],
1 => [ 'url_fichier' => 'bar' ]
]
]
];
Then, copy/paste the following code and you will be able to execute it:
$values_to_insert_in_meta_table = [];
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$last_depth = 0;
$key_in_db_format = [];
foreach ($iterator as $value_key_field => $value_value_field) {
if($iterator->getDepth() == $last_depth) {
array_pop($key_in_db_format);
}
if(is_array($value_value_field)) {
array_push($key_in_db_format, $value_key_field);
} else {
$values_to_insert_in_meta_table[implode('_', $key_in_db_format)] = $value_value_field;
}
$last_depth = $iterator->getDepth();
}
echo '<pre>';
print_r($values_to_insert_in_meta_table);
Maybe I missed something, but as far as I understand, I would do something like that:
<?php
function flatten(array $array, ?string $prefix = null): array {
$prefix = $prefix === null ? '' : "{$prefix}_";
$output = [];
foreach ($array as $key => $value) {
$key = $prefix . $key;
if (is_array($value)) {
$output = array_merge($output, flatten($value, $key));
} else {
$output[$key] = $value;
}
}
return $output;
}
var_export(flatten([
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]));
Output:
array (
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty',
)
I think I've found a solution!!! :-)
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$key_in_db_format = [];
$current_counter = 0;
foreach($iterator as $value_key_field => $value_value_field) {
if(is_array($value_value_field)) {
$current_counter = 0;
array_push($key_in_db_format, $value_key_field);
}
if(!is_array($value_value_field)) {
$key_to_insert_in_db = !empty($key_in_db_format) ? implode('_', $key_in_db_format) . '_' . $value_key_field : $value_key_field ;
$values_to_insert_in_meta_table[$key_to_insert_in_db] = $value_value_field;
if($current_counter == count($iterator->getSubIterator())) {
array_pop($key_in_db_format);
}
$current_counter++;
}
}
echo '<br /> <pre>';
print_r($values_to_insert_in_meta_table);
exit;
The idea is:
We add to the array of ascendent keys the key if, and only if, the current element is not a leaf.
If the current element is a leaf, then we define the key equalled to the imploded ascendent keys PLUS (concatenation) the current element's key. Moreover we pop the array of ascendent keys if there are not following siblings elements.

PHP array_filter on array containing multiple arrays

I'm using array_filter in PHP to split an array containing multiple arrays when the value of a key named type matches a specific string. Here's what this looks like:
Sample Array
$arr[] = Array (
[0] => Array ( [type] => Recurring ... )
[1] => Array ( [type] => Single ... )
)
Functions
function recurring($value)
{
return ($value['type'] == 'Recurring');
}
function single($value)
{
return ($value['type'] == 'Single');
}
Split Arrays
$recurring = array_filter($arr, 'recurring');
$single = array_filter($arr, 'single');
This works, but I was curious if there was a way to simplify it so that I could create additional filtered arrays in the future without creating a new function for each.
I've started setting up a single function using a closure, but I'm not sure how to do it. Any ideas?
function key_type($value, $key, $string) {
return $key == 'type' && $value == $string;
}
$recurring = array_filter($arr,
key_type('Recurring'), ARRAY_FILTER_USE_BOTH);
$single = array_filter($pricing,
key_type('Single'), ARRAY_FILTER_USE_BOTH);
You could actually do what you proposed in your question. You just need to have the key_type() function return a callable function, which is what array_filter expects as the second parameter. You can return an anonymous function and pass the argument into the anonymous function using the use keyword as CBroe mentioned in the comments.
Here is an example:
function key_type($key) {
return function($value) use ($key) {
return $value['type'] == $key;
};
}
$arr = array(
array('type'=>'Recurring'),
array('type'=>'Single')
);
print_r(array_filter($arr, key_type('Single'), ARRAY_FILTER_USE_BOTH));
The above code will output:
Array ( [1] => Array ( [type] => Single ) )
The beauty of this method is that if you need to change the logic for all instances where you need to use your filter, you just have to change it one time in your key_type function.
An approach would be like below, however I don't like it honestly.
$array = [['type' => 'Single'], ['type' => 'Recurring']];
function key_type($value) {
global $string;
return $value['type'] == $string;
}
($string = 'Recurring') && ($recurring = array_filter($array, 'key_type'));
($string = 'Single') && ($single = array_filter($array, 'key_type'));
Another way to achieve same thing is using Anonymous functions (closures). Don't think much about being DRY it seems nice:
$array = [['type' => 'Single'], ['type' => 'Recurring']];
$recurring = array_filter($array, function($value) {
return $value['type'] == 'Recurring';
});
$single = array_filter($array, function($value) {
return $value['type'] == 'Single';
});
This task might be more about grouping than filtering -- it is difficult to discern from the limited sample data.
As a general rule, I strongly advise against using variable variables in PHP code. It is better practice to store data in arrays for accessibility reasons.
If you only have the two mentioned type values in your project data, then the conditional can be removed entirely.
Code: (Demo)
$array = [
['type' => 'Recurring', 'id' => 1],
['type' => 'Single', 'id' => 2],
['type' => 'Other', 'id' => 3],
['type' => 'Recurring', 'id' => 4],
['type' => 'Single', 'id' => 5],
];
$result = [];
foreach ($array as $row) {
if (in_array($row['type'], ['Recurring', 'Single'])) {
$result[strtolower($row['type'])][] = $row;
}
}
var_export($result);
Output:
array (
'recurring' =>
array (
0 =>
array (
'type' => 'Recurring',
'id' => 1,
),
1 =>
array (
'type' => 'Recurring',
'id' => 4,
),
),
'single' =>
array (
0 =>
array (
'type' => 'Single',
'id' => 2,
),
1 =>
array (
'type' => 'Single',
'id' => 5,
),
),
)

How to check if duplicate values of some indexes exist in multi dimensional array or not?

My problem is i have a multidimensional array posting from form to php, now i want to checking if duplicate values of some indexes exist in multi dimensional array or not?
e.g:
$data=Array
(
0 => Array
(
uid => '100',
name => 'Sandra Shush',
type => 'abc'
),
1 => Array
(
uid => '101',
name => 'Sandra Shushtext',
type => 'xyz'
),
2 => Array
(
uid => '100',
name => 'Sandra Shush',
type => 'abc'
)
);
here name and type of index 1 and 2 are same, so how can i check it?
I am familiar with
$key = array_search('abc', array_column($data, 'type'));
but it is for duplication of single column value in multi rows, in my situation if multi column of same rows same with multi column of any other row then record will be consider as duplicate.
Any help should be appreciated, Thanks in advance.
You can try using array_reduce by creating a key using your desired item keys:
$result = array_reduce($data, function ($carry, $item) {
$key = $item['uid'] . $item['type'];
$item['is_duplicate'] = isset($carry[$key]);
if ($item['is_duplicate']) {
$carry[] = $item;
} else {
$carry[$key] = $item;
}
return $carry;
}, []);
var_dump($result);
The easiest way, well at least the one I would use is to encode your arrays into md5 (or any other kind) string and compare those values. I think it is the most efficient in your case.
Example:
<?php
function arrayToString($array) {
$str = '';
if ( !is_array($array) )
return $str;
foreach ( $array as $key => $val ) {
$str .= $key . ':' . $val;
}
return $str;
}
$data=Array
(
0 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'abc'
),
1 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'xyz'
),
2 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'abc'
)
);
$temp = array();
foreach ( $data as $d ) {
array_push($temp, md5(arrayToString($d)));
}
$unique = array_unique($temp);
var_dump($unique); // prints unique array
This is a very fast designed approach and will find duplicates. Note that duplicates are elements which have the same value for the same key. So if any of uid, name or type match, they will be treated as duplicates. Therefore I adjust the third array element, because all elements in your array share the same values.
$data = [
....
2 =>
[
'uid' => '200',
'name' => 'Mandra Shush',
'type' => 'abcx'
]
];
$duplicates = [];
$valuesToCompare = ["uid", "name", "type"];
function equals($value, $toCompare, $keysToCompare)
{
foreach ($keysToCompare as $compareKey) {
if ($value[$compareKey] === $toCompare[$compareKey]) {
return true;
}
}
return false;
}
foreach ($data as $index => $element) {
foreach ($data as $indexInner => $elementToCompare) {
if ($index !== $indexInner) {
if (equals($element, $elementToCompare, $valuesToCompare)) {
$duplicates[] = [$index => $indexInner];
}
}
}
}
var_dump($duplicates);
This will output the following, which indicates we found 2 duplicates. Where element 0 is duplicate of 1, and 1 is duplicate of 0.
array (size=2)
0 =>
array (size=1)
0 => int 1
1 =>
array (size=1)
1 => int 0
I achieved above scenario like this:
Dont know which one is best mine or other's who posted answers.
foreach($data as $key => $row)
{
$combinedarr[] = array("name"=>$row["name"],"type"=>$row["type"]);
}
//chck if same facilitiy is being visit on same date twice
$countcomb = count($combinedarr);
$uniquearr = array_unique($combinedarr, SORT_REGULAR);
if($countcomb==count($uniquearr)){
}else{
//yes duplicate exists
};
Thanks again for those who answered.

How do I reform this array into a differently structured array

I have an array that looks like this:
[0] => Array
(
[name] => typeOfMusic
[value] => this_music_choice
)
[1] => Array
(
[name] => myMusicChoice
[value] => 9
)
[2] => Array
(
[name] => myMusicChoice
[value] => 8
)
I would like to reform this into something with roughly the following structure:
Array(
"typeOfMusic" => "this_music_choice",
"myMusicChoice" => array(9, 8)
)
I have written the following but it doesn't work:
foreach($originalArray as $key => $value) {
if( !empty($return[$value["name"]]) ){
$return[$value["name"]][] = $value["value"];
} else {
$return[$value["name"]] = $value["value"];
}
}
return $return;
I've tried lots of different combinations to try and get this working. My original array could contain several sets of keys that need converting to arrays (i.e. it's not always going to be just "myMusicChoice" that needs converting to an array) ?
I'm getting nowhere with this and would appreciate a little help. Many thanks.
You just need to loop over the data and create a new array with the name/value. If you see a repeat name, then change the value into an array.
Something like this:
$return = array();
foreach($originalArray as $data){
if(!isset($return[$data['name']])){
// This is the first time we've seen this name,
// it's not in $return, so let's add it
$return[$data['name']] = $data['value'];
}
elseif(!is_array($return[$data['name']])){
// We've seen this key before, but it's not already an array
// let's convert it to an array
$return[$data['name']] = array($return[$data['name']], $data['value']);
}
else{
// We've seen this key before, so let's just add to the array
$return[$data['name']][] = $data['value'];
}
}
DEMO: https://eval.in/173852
Here's a clean solution, which uses array_reduce
$a = [
[
'name' => 'typeOfMusic',
'value' => 'this_music_choice'
],
[
'name' => 'myMusicChoice',
'value' => 9
],
[
'name' => 'myMusicChoice',
'value' => 8
]
];
$r = array_reduce($a, function(&$array, $item){
// Has this key been initialized yet?
if (empty($array[$item['name']])) {
$array[$item['name']] = [];
}
$array[$item['name']][] = $item['value'];
return $array;
}, []);
$arr = array(
0 => array(
'name' => 'typeOfMusic',
'value' => 'this_music_choice'
),
1 => array(
'name' => 'myMusicChoice',
'value' => 9
),
2 => array(
'name' => 'myMusicChoice',
'value' => 8
)
);
$newArr = array();
$name = 'name';
$value = 'value';
$x = 0;
foreach($arr as $row) {
if ($x == 0) {
$newArr[$row[$$name]] = $row[$$value];
} else {
if (! is_array($newArr[$row[$$name]])) {
$newArr[$row[$$name]] = array();
}
array_push($newArr[$row[$$name]], $row[$$value]);
}
$x++;
}

Get previous and next values in array

I have array, for example:
<?php
$array = array(
0 => array(
'subject' => 'Stackoverflow',
'body' => '',
'name' => 'php'
),
1 => array(
'subject' => 'Test',
'body' => 'Wayne',
'name' => ''
),
2 => array(
'subject' => '',
'body' => 'this is ok',
'name' => ''
),
3 => array(
'subject' => 'cnn',
'body' => 'Google',
'name' => 'private'
),
4 => array(
'subject' => 'code',
'body' => '',
'name' => '7777'
)
);
And i would like get subject, body and name for key 2 and if key not exist then this should get from previous and next (separate function) values.
For example if i want get value from 2 key:
function getCurrentOrPrevious(2);
should return:
array(
'subject' => 'Test', //from [1]
'body' => 'this is ok', //from [2] - current and exist
'name' => 'php' //from [0] - in from [2] and [1] not exist
)
function getCurrentOrNext(2);
should return:
array(
'subject' => 'cnn', //from [3]
'body' => 'this is ok', //from [2] - current
'name' => 'php' //from [3]
)
How is the best way for this? Are there any functions in PHP for such operations?
LIVE
Assuming, you fill your array similar to $array[] = $value; [i.e. that you have consecutive numeric keys starting from zero]:
## $array is the array to take values from, $key is the target key
## $fields are required fields
function getCurrentOrPrevious($array, $key, $fields) {
if ($key < 0) return null;
$output = array();
foreach ($fields as $field) {
for ($i = $key; $i >= 0; $i--) {
if (!empty($array[$i][$field])) {
$output[$field] = $array[$i][$field];
break;
}
}
return $output;
}
Use as follows:
$my_values = getCurrentOrPrevious($array, 12, array('subject', 'body', 'name'));
I guess you could use empty() function of php to check. Also you should think about how far that function shell go?
What if [3] (next) also has an empty value at the same position
What if previous index is <0?
UPDATE
function getCurrOrNext($array, $index){
$keys = array_keys($array[$index]);
foreach($keys AS $key){
if($array[$index][$key] == "") {
$array[$index][$key] = (!empty($array[$index+1]) && !empty($array[$index+1][$key])?$array[$index+1][$key]:null);
}
}
return $array;
}
I guess something like this
function getCurrentOrPrev($array, $key) {
while(key($array)!==$key) next($array); //move internal pointer to required position first
$result = current($array);
//loop is going to execute as long as at least one of elements is empty and we didn't get to beginning of array yet
while((empty($result['subject'])
|| empty($result['body'])
|| empty($result['name']))
&& prev($array)!==false) {
$c = current($array);
//replace empty elements with values of current element
$result['subject'] = empty($result['subject']) ? $c['subject'] : '';
$result['body'] = empty($result['body']) ? $c['body'] : '';
$result['name'] = empty($result['name']) ? $c['name'] : '';
}
return $result;
}
For function with next simply replace prev() method with next(), or to minimize code duplication You may introduce third parameter and call correct method based on its value.
This method doesn't care about values of the key other but the key specified in parameter.
You may have mixed literal and numeric indexes.

Categories