Recursive function for checking multidimensional arrays - php

Really struggling with this. I have a multidimensional array n levels deep. Each 'array level' has information I need to check (category) and also check if it contains any arrays.
I want to return the category ids of all the arrays which have a category and do not contain an array (i.e. the leaves). I can echo output properly, but I am at a loss as how to return the ids in an array (without referencing)
I have tried RecursiveIteratorIterator::LEAVES_ONLY and RecursiveArrayIterator but I don't think they work in my case? (Maybe I am overlooking something)
$array
array(2) {
["1"]=>
string(5) "stuff"
["2"]=>
array(2) {
["category"]=>
string(1) "0"
["1"]=>
array(3) {
[0]=>
array(3) {
["category"]=>
string(1) "1"
["1"]=>
string(5) "stuff"
["2"]=>
string(5) "stuff"
}
[1]=>
array(5) {
["category"]=>
string(1) "2"
["1"]=>
string(5) "stuff"
["2"]=>
string(5) "stuff"
}
[1]=>
array(5) {
["1"]=>
string(5) "stuff"
["32"]=>
string(5) "stuff"
}
}
}
}
My function
public function recurs($array, $cats = [])
{
$array_cat = '';
$has_array = false;
// Check if an id exists in the array
if (array_key_exists('category', $array)) {
$array_cat = $array['category'];
}
// Check if an array exists within the array
foreach ($array as $key => $value) {
if (is_array($value)) {
$has_array = true;
$this->recurs($value, $cats);
}
}
// If a leaf array
if (!$has_array && is_numeric($array_cat)) {
echo "echoing the category here works fine: " . $array_cat . "\n";
return $array_cat;
}
}
Calling it
$cats_array = $this->recurse($array)
Output echoed
echoing the category here works fine: 1
echoing the category here works fine: 2
How do I return the ids in an array to use in the $cats_array variable?
EDIT: The output should match the echo, so I should get an array containing (1, 2) since they are the only arrays with categories and no arrays within them
array(2){
[0]=>
int(1) "1"
[1]=>
int(1) "2"
}

If I understood you correctly this function will do the job:
function getCategories(array $data)
{
if ($subArrays = array_filter($data, 'is_array')) {
return array_reduce(array_map('getCategories', $subArrays), 'array_merge', array());
};
return array_key_exists('category', $data) ? array($data['category']) : array();
}
If the array contains any sub-arrays they will be returned by array_filter and you will enter the if statement. array_map will apply the function recursively to the sub-arrays and array_reduce will merge the results.
If the array doesn't contain any sub-arrays you will not enter the if statement and the function will return an array with the category if it is present.
Note that you might need to use array_unique to return unique categories.
Also for small performance optimization instead of array_key_exists you can use isset($array[$key]) || array_key_exists($key, $array).
Update
If you want to update your function to make it work you have to recursively collect and merge the sub results. In the following example I introduced a new variable for this:
public function recurs($array, $cats = [])
{
$result = [];
$array_cat = '';
$has_array = false;
// Check if an id exists in the array
if (array_key_exists('category', $array)) {
$array_cat = $array['category'];
}
// Check if an array exists within the array
foreach ($array as $key => $value) {
if (is_array($value)) {
$has_array = true;
$result = array_merge($result, $this->recurs($value, $cats));
}
}
// If a leaf array
if (!$has_array && is_numeric($array_cat)) {
echo "echoing the category here works fine: " . $array_cat . "\n";
return [$array_cat];
}
return $result;
}

Related

Remove duplicates from PHP which has multiple arrays [duplicate]

This question already has answers here:
Filter/Remove rows where column value is found more than once in a multidimensional array
(4 answers)
Closed 9 months ago.
This is the current output I have from an array by doing var_dump($getvals):
array(2) {
["value"]=>string(5) "150mm"
["label"]=>string(5) "150mm"
}
array(2) {
["value"]=>string(5) "150mm"
["label"]=>string(5) "150mm"
}
array(2) {
["value"]=>string(5) "150mm"
["label"]=>string(5) "150mm"
}
array(2) {
["value"]=>string(5) "150mm"
["label"]=>string(5) "150mm"
}
array(2) {
["value"]=>string(5) "125mm"
["label"]=>string(5) "125mm"
}
array(2) {
["value"]=>string(5) "125mm"
["label"]=>string(5) "125mm"
}
What I want to achieve is first of all, to ignore the ['label'] tables and then try removing any duplicates (150mm and 125mm). I have tried both array_unique and !inarray() but it won't work. Here is my code:
$getvals = get_sub_field('option_diameter'); // This is my array
$takens = array(); // I create an empty array for later on
foreach ($getvals as $key => $getval){ // Running through the array
if ($key == 'value'){ // Ignoring Label table
if(!in_array($getval, $takens)){ // Check if the current value is already inside $takens array
$takens[] = $getval; // If not, then put it
}
}
}
var_dump($takens); // The output is below
This is what I get from var_dump($takens) :
array(1) {
[0]=>string(5) "150mm"
}
array(1) {
[0]=>string(5) "150mm"
}
array(1) {
[0]=>string(5) "150mm"
}
array(1) {
[0]=>string(5) "150mm"
}
array(1) {
[0]=>string(5) "125mm"
}
array(1) {
[0]=>string(5) "125mm"
}
The duplicate values are not removed. Any ideas?
Use a key to remove the duplicate, Demo
$result = [];
foreach($array as $arr){
$result[$arr['value']] = $arr;
}
print_r(array_values($result));
You need to first fetch value by using array_diff_key. Then you need to use array_column, in which passing null as second parameter and unique by value as key to removing duplicates(as keys can not be duplicated). Then to reset indexes array_values
Here is the script,
$takens = array_values(array_column(array_map(function ($a) {
return array_diff_key($a, ['label' => '']);
}, $getvals), null, 'value'));
Demo
Output:-
array(2) {
[0]=>
array(1) {
["value"]=>
string(5) "150mm"
}
[1]=>
array(1) {
["value"]=>
string(5) "125mm"
}
}
You can use array_column
$r = array_values(array_column($a, null, 'label'));
OR
if you want to keep single column
$r = array_values(array_column($a, 'value', 'label'));
print_r($r);
array_values will reorder the keys of the array
Working example :- https://3v4l.org/n8SA1
It look like you forgot a foreach.
Here a test done a few minutes ago :
$aArrayTest = array (
array(
'value'=>'150mm',
'label'=>'150mm'
),
array(
'value'=>'150mm',
'label'=>'150mm'
),
array(
'value'=>'150mm',
'label'=>'150mm'
),
array(
'value'=>'150mm',
'label'=>'150mm'
),
array(
'value'=>'125mm',
'label'=>'125mm'
),
array(
'value'=>'125mm',
'label'=>'125mm'
)
);
$aArrayResult = array();
foreach ($aArrayTest as $aArray) { // Running through the array of Arrays
foreach ($aArray as $key => $value){ // Running through each array
if ($key == 'value'){ // Ignoring Label table
if(!in_array($value, $aArrayResult)){ // Check if the current value is already inside $takens array
$aArrayResult[] = $value; // If not, then put it
}
}
}
}
var_dump($aArrayResult);</pre>

PHP Change multidimensional array structure

Hello I've multidimensional array that looks like that:
array(13890) {
[0]=>
array(2) {
["Icd"]=>
array(2) {
["id"]=>
int(111)
["nazwa"]=>
string(6) "DŻUMA"
}
["ProjectIcd"]=>
array(0) {
}
}
[1]=>
array(2) {
["Icd"]=>
array(2) {
["id"]=>
int(566)
["nazwa"]=>
string(7) "ŚWINKA"
}
["ProjectIcd"]=>
array(0) {
}
}
An so on.
I want to change it so it looks something like that:
array(13890) {
[0]=> array(2) {
["id"]=>
int(111)
["text"]=>
string(6) "DŻUMA"
}
How is this possible to do?
I want to add, I want to convert the array to json and feed it to select2 js in ajax.
Will that be a problem or not?
Short solution using array_map function:
// $arr is your initial array
$new_arr = array_map(function($a){
return ['id' => $a['Icd']['id'], 'text' => $a['Icd']['nazwa']];
}, $arr);
So you can simple create a new array and add there the values, which you want based on the old array. Then you convert the array to a json string with the php function json_encode():
$array = array("text"=>$old_array[0]["Icd"]["nazwa"]);
echo json_encode($array);
I hope this is something that you want.
$res = [];
$i = 0;
foreach($array as $arr) {
//get keys
if (count($arr) > 0) {
$keys = array_keys($arr);
// loop through the keys and fetch data of those keys
// put in array
foreach($keys as $key) {
if ($arr[$key]) {
$res[$i]['id'] = $arr[$key]['id'];
$res[$i]['text'] = $arr[$key]['nazwa'];
}
$i++;
}
}
}
print_r($res);
// To change array to json
echo json_encode($res);

Remove duplicate array where key->value appears twice - leave just one array

Removing an array if there is a duplicate in inner array for example - As we see there is [0]['user'] and its 1, the same appears in the array 1, my desired array would only contain one of the arrays it doesn't matter which one - also would be nice if I would get a returned message that there were duplicates, the array length can vary from 1 to 10 for example. I tried some codes provided already here on stackoverflow for unique multidementional arrays but non seems to be working for me.
And this is the html method. The users can duplicate because of selection of the same user name
array(3) {
[0]=>
array(6) {
["user"]=>
string(1) "1"
["role"]=>
string(1) "1"
["can_edit"]=>
NULL
["can_read"]=>
NULL
["can_execute"]=>
NULL
["is_admin"]=>
NULL
}
[1]=>
array(6) {
["user"]=>
string(1) "1"
["role"]=>
string(1) "2"
["can_edit"]=>
NULL
["can_read"]=>
NULL
["can_execute"]=>
NULL
["is_admin"]=>
NULL
}
}
Code used for the example output
foreach ($this->input->post() as $key => $value)
{
if(preg_match("/^user.{1,2}$/",$key)>0) {
$postvars[] = $key;
}
if(preg_match("/^user.{1,2}$/",$key)>0) {
$postvalues[] = $value;
}
}
$filterArray = array_combine($postvars, $postvalues);
function array_unique_multidimensional($input)
{
$serialized = array_map('serialize', $input);
$unique = array_unique($serialized);
return array_intersect_key($input, $unique);
}
foreach (array_unique_multidimensional($postvars) as $key)
{
preg_match("|\d+|", $key, $m);
$user = $filterArray[$key];
$role = $this->input->post('role'.$m[0]);
$can_edit = $this->input->post('can_edit'.$m[0]);
$can_read = $this->input->post('can_read'.$m[0]);
$can_execute = $this->input->post('can_execute'.$m[0]);
$is_admin = $this->input->post('is_admin'.$m[0]);
$records[] = array('user' => $user,'role'=>$role,'can_edit'=>$can_edit,'can_read' =>$can_read,'can_execute' =>$can_execute,'is_admin'=>$is_admin);
}
var_dump($records);
var_dump(array_unique_multidimensional($records));

php count+array_filter multiple values within a multi dimensional array [duplicate]

This question already has answers here:
array_count_values() with objects as values
(3 answers)
Closed 6 months ago.
How can I prevent duplicating the same block of code for each value that I want to search for?
I want to create a new array ($result) by counting for specific values within another multi-dimensional array ($data).
$result = array();
$result['Insulin'] = count(array_filter($data,function ($entry) {
return ($entry['choice'] == 'Insulin');
}
)
);
$result['TZD'] = count(array_filter($data,function ($entry) {
return ($entry['choice'] == 'TZD');
}
)
);
$result['SGLT-2'] = count(array_filter($data,function ($entry) {
return ($entry['choice'] == 'SGLT-2');
}
)
);
$data array example:
array(2) {
[0]=>
array(9) {
["breakout_id"]=>
string(1) "1"
["case_id"]=>
string(1) "1"
["stage_id"]=>
string(1) "1"
["chart_id"]=>
string(1) "1"
["user_id"]=>
string(2) "10"
["region"]=>
string(6) "Sweden"
["choice"]=>
string(7) "Insulin"
["switched_choice"]=>
NULL
["keep"]=>
string(1) "1"
}
[1]=>
array(9) {
["breakout_id"]=>
string(1) "1"
["case_id"]=>
string(1) "1"
["stage_id"]=>
string(1) "1"
["chart_id"]=>
string(1) "1"
["user_id"]=>
string(1) "7"
["region"]=>
string(6) "Sweden"
["choice"]=>
string(7) "Insulin"
["switched_choice"]=>
NULL
["keep"]=>
string(1) "1"
}
}
You may convert your anonymous function into a closure with the use keyword. Pass in a string variable for the value you want to match.
// Array of strings to match and use as output keys
$keys = array('Insulin','TZD','SGLT-2');
// Output array
$result = array();
// Loop over array of keys and call the count(array_filter())
// returning the result to $result[$key]
foreach ($keys as $key) {
// Pass $key to the closure
$result[$key] = count(array_filter($data,function ($entry) use ($key) {
return ($entry['choice'] == $key);
}));
}
Converting your var_dump() to an array and running this against it, the output is:
Array
(
[Insulin] => 2
[TZD] => 0
[SGLT-2] => 0
)
You can simplify it with array_count_values() as well:
$result2 = array_count_values(array_map(function($d) {
return $d['choice'];
}, $data));
print_r($result2);
Array
(
[Insulin] => 2
)
If you need zero counts for the missing ones, you may use array_merge():
// Start with an array of zeroed values
$desired = array('Insulin'=>0, 'TZD'=>0, 'SGLT-2'=>0);
// Merge it with the results from above
print_r(array_merge($desired, $result2));
Array
(
[Insulin] => 2
[TZD] => 0
[SGLT-2] => 0
)
Not the most efficient algorithm in terms of memory, but you can map each choice onto a new array and then use array_count_values():
$result = array_count_values(array_map(function($item) {
return $item['choice'];
}, $data));
Since 5.5 you can simplify it a little more by using array_column():
$result = array_count_values(array_column($data, 'choice'));
You can simplify your code easily if counting is your only goal :
$result = array();
foreach ($data AS $row) {
if (!isset($result[$row['choice']])) {
$result[$row['choice']] = 1;
} else {
$result[$row['choice']]++;
}
}
If you want to count only specific choices, you can change the above code into something like this :
$result = array();
$keys = array('Insulin', 'TZD', 'SGLT-2');
foreach ($data AS $row) {
if (!in_array($row['choice'], $keys)) {
continue;
}
if (!isset($result[$row['choice']])) {
$result[$row['choice']] = 1;
} else {
$result[$row['choice']]++;
}
}

Group and count nested arrays PHP

I have an array that groups different items by item type. I am grouping the result by category_id field. What I want is the output to be
item1 = 3
item2 = 2
My array looks like this if I do a var_dump()
array(2) {
["item1"]=>
array(3) {
[0]=>
string(1) "3"
[2]=>
string(1) "5"
[4]=>
string(1) "7"
}
["item2"]=>
array(2) {
[1]=>
string(1) "4"
[3]=>
string(1) "6"
}
}
Here is the code I am using:
$items = Item::where('order_id','=',$payload["orderId"])->get();
$itemsGrouped = [];
$count = 0;
foreach($items as $item){
$itemsGrouped[$item->category_id][$count] = $item->id;
$count++;
}
foreach($itemsGrouped as $grp){
echo key($itemsGrouped).'='.count($grp).'<br>';
};
And here is what I am currently getting. The count is working but not the $itemsGrouped key. It is duplicated.
item2=3<br>item2=2<br>
Change your code as below
foreach($itemsGrouped as $key => $grp){
echo $key.'='.count($grp).'<br>';
};
In order to use key() function, you need to traverse the array using next/current function
foreach($itemsGrouped as $key => $grp){
echo $key.'='.count($grp).'<br>';
};
key() function returns the current element's key, which is defined by an array's internal pointer. Obviously it always points to the last element.
$myarray = "Your array";
$count = array(); // create an empty array
foreach($myarray as $arr) {
foreach($arr as $a) {
$key = array_keys($a);
$count[$key[0]]++;
}
}
print_r($count);

Categories