How to iterate an array to count elements PHP? - php

I wanna know how to iterate an array to count elements in PHP, I've trying next,
foreach ($items as $item) {
$tm[$item->provider->name] = [];
foreach ($items as $item) {
$tm[$item->provider->name][$item->product->brand->name] = isset($tm[$item->provider->name][$item->product->brand->name]) ? $tm[$item->provider->name][$item->product->brand->name] + 1 : $tm[$item->provider->name][$item->product->brand->name] = 1;
}
}
But I get a wrong result, I get an array but I get a very high number count as if iterated many timesmany times
The structure of the array is as follows
[{
"id": 1,
"product": {
"id": 1,
"brand": {
"id": 1,
"name": "iphone"
}
},
"provider": {
"id": 1,
"name": "at&t"
}
},
{
"id": 2,
"product": {
"id": 2,
"brand": {
"id": 2,
"name": "iphone"
}
},
"provider": {
"id": 1,
"name": "at&t"
}
},
{
"id": 3,
"product": {
"id": 3,
"brand": {
"id": 3,
"name": "iphone"
}
},
"provider": {
"id": 1,
"name": "t-mobile"
}
}]

IIUC, you are trying to count the product->brand->name values for each provider->name. You can do that using this code:
$tm = array();
foreach ($items as $item) {
$product = $item->product->brand->name;
$provider = $item->provider->name;
$tm[$provider][$product] = ($tm[$provider][$product] ?? 0) + 1;
}
print_r($tm);
Output (for your sample data):
Array
(
[at&t] => Array
(
[iphone] => 2
)
[t-mobile] => Array
(
[iphone] => 1
)
)
Demo on 3v4l.org

From your code example you apparently want to count how many occurrences are for each "thing" in your array, otherwise count() would have probably sufficed...
Though you are doing a lot of work to pre-init the counting array - that is completely unneeded: read about auto-vivifcation.
Code like so would probably suffice (I'm not following your example code here - you'd need to work out to your needs):
$counters = [];
foreach ($items as $item)
#$counters[$item->name][$item->model]++;

Related

How to add a new item to every index in the array -php?

var_export($res) is an array as below.
array(0 =>(object) array('user' => NULL,
'courseId' => 18,),
1 =>(object) array('user' =>(object) array('id' => 1,
'name' => 'admin',
'files' => NULL,),
'courseId' => 1,),
)
At every index in this array, i need to calculate count using the courseId and add a new item called count to every index. i used the below code. And the expected end result is an array of objects , not an object of objects.
$res=json_decode($response);
foreach ($res as $key ) {
$count = MyCourse::where('course_id', $key->courseId)->distinct('student_id')->count();
$res['count'] = $count;
}
return response()->json(['data' => $res,'statusCode' => 200], 200);
the above code shows the below data. it added count as a new index in the array not added it as anew item to every index of the array. Also, it is returning an result in the form of object of objects.How can i fix this?
{
"0": {
"user": null,
"courseId": 18
},
"1": {
"user": {
"id": 1,
"name": "admin",
"files": null
},
"courseId": 1
},
"count": 1
}
Expected End Result:
[
{
"user": null,
"courseId": 18,
"count": 20
},
{
"user": {
"id": 1,
"name": "admin",
"files": null
},
"courseId": 1,
"count": 10
}
]
In your foreach loop, $key is each of your objects containing courseId.
Instead of adding count to $res, add it to $key like so:
foreach ($res as &$key ) {
$count = MyCourse::where('course_id', $key->courseId)->distinct('student_id')->count();
$key->count = $count; // set it to $key instead of $res
}
If you want the results to be arrays instead of objects, pass true as the second argument to json_decode like so:
$res = json_decode($response, true); // associative array instead of object
and then edit the foreach to handle it as arrays instead:
foreach ($res as $key ) {
$count = MyCourse::where('course_id', $key['courseId'])->distinct('student_id')->count();
$key['count'] = $count; // set it to $key instead of $res
}
In your case you want to set the count field for $key instead of $res
Your foreach loop should look like this:
foreach ($res as $key ) {
$count = MyCourse::where('course_id', $key->courseId)->distinct('student_id')->count();
$key->count = $count;
}
This would output:
{
"0": {
"user": null,
"courseId": 18,
"count": 20
},
"1": {
"user": {
"id": 1,
"name": "admin",
"files": null
},
"courseId": 1,
"count": 10
}
}

Find latest children in nested categories php

I tried to parse a JSON using PHP from url. I need a list of item that have no children - means children field should be empty array and parent_id shouldn't be 0 - means not be parents.
JSON:
{
"body": {
"data": [
{
"id": 1,
"name": "car",
"parent_id": "0",
"children": [
{
"id": 2,
"name": "bmw",
"parent_id": "1",
"children": [
{
"id": 4,
"name": "bmw i8",
"parent_id": "2",
"children": []
}
]
},
{
"id": 3,
"name": "mustang",
"parent_id": "1",
"children": []
}
]
},
{
"id": 5,
"name": "clothes",
"parent_id": "0",
"children": []
},
{
"id": 6,
"name": "mobile",
"parent_id": "0",
"children": [
{
"id": 7,
"name": "apple",
"parent_id": "6",
"children": [
{
"id": 9,
"name": "Iphone 12 pro",
"parent_id": "7",
"children": []
},
{
"id": 10,
"name": "Iphone 11",
"parent_id": "7",
"children": []
}
]
},
{
"id": 8,
"name": "Xiaomi",
"parent_id": "6",
"children": []
}
]
}
]
}
}
Output Expected:
[4,3,9,10,8]
This is my php code that i tried and doesn't work.
$CategoryUrl = file_get_contents(self::CATEGORY_URL,true);
$array = json_decode($CategoryUrl,true);
$list = array();
foreach( $array['body']['data'] as $value ){
if (($value['parent_id'] != 0) && empty($value['children'])) {
foreach( $value['children'] as $val ){
$list[] = $val;
}
}
}
print_r($list);
It is indeed a little tricky to keep track of parent ID values and children call using array_walk_recursive as it jumps directly to its children. However, this can be still accomplished with your own recursive version like below. Keep checking with parent_id and children count. If both constraints satisfy, add them to $result, else keep calling children recursively.
<?php
$str = '{"body":{"data":[{"id":1,"name":"car","parent_id":"0","children":[{"id":2,"name":"bmw","parent_id":"1","children":[{"id":4,"name":"bmw i8","parent_id":"2","children":[]}]},{"id":3,"name":"mustang","parent_id":"1","children":[]}]},{"id":5,"name":"clothes","parent_id":"0","children":[]},{"id":6,"name":"mobile","parent_id":"0","children":[{"id":7,"name":"apple","parent_id":"6","children":[{"id":9,"name":"Iphone 12 pro","parent_id":"7","children":[]},{"id":10,"name":"Iphone 11","parent_id":"7","children":[]}]},{"id":8,"name":"Xiaomi","parent_id":"6","children":[]}]}]}}';
$data = json_decode($str,true);
$result = [];
function walkRecursive($data,&$result){
foreach($data as $entry){
if($entry['parent_id'] != 0 && count($entry['children']) == 0){
$result[] = $entry['id'];
}else{
walkRecursive($entry['children'],$result);
}
}
}
walkRecursive($data['body']['data'],$result);
print_r($result);
We can solve this problem by function call recursive algorithm,
hope it clear for you
$CategoryUrl = file_get_contents(self::CATEGORY_URL,true);
$array = json_decode($CategoryUrl, true);
$items = $array['body']['data'];
$list = [];
findParentIds(items, $list);
// doSomething
print_r($list);
/**
* passe by ref (list)
* #param array $children
* #param $list
*/
function findParentIds(array $children, & $list)
{
foreach ($children as $child) {
// use case 1: has no children and parent_id is 0 , just continue
if (empty($child['children']) && $child['parent_id'] == "0") {
continue;
}
// use case 2: has no children and parent_id is 0 , it's parent item
if (empty($child['children']) && $child['parent_id'] != "0") {
array_push($list, $child['id']);
}
// has children and parent_id is not 0 , recall function to treat use case 1 and 2 ..
findParentIds($child['children'], $list);
}
}

Generate Nested Tree Structure or Hierarchy based on parent child relationship using recursion - PHP

I've been stuck in this since yesterday any help will be much appreciated. The situation is this: I have a table for navigation links with the following structure,
id | parent_id | text
In my scenario the parent_id may be null(no sub-menu) or any id pointing back to the table which will make the record a sub-menu of the parent. I've tried many different ways to execute this but with no luck, the closest scenario I came up with is the following,
// All navigation link results are stored in $data
public static function transform(array &$data) {
$result = [];
$search = function(&$array = [], $parent = null) use (&$search, &$result){
foreach($array as $key => $value) {
/** If the element has an ancestor */
if($parent && $parent->id == $value->parent_id) {
$next = $parent->children[] = $value;
$search($array, $next);
unset($array[$key]);
}
}
return $parent;
};
foreach($data as $val) {
$result[] = $search($data, $val);
}
return $result;
}
The function works fine and tracks all the ancestors of a certain link but in the next iteration is tracking all the ancestors of the next element(even if it was a child of the previous one) so it comes back with a lot of duplicated data. I came up with the solution to unset the iterated element from the array but for some reason, the element is still being used in the next iteration.
Here is my current data
Here is my current result
A simple concept for faster search and to process one item only once.
Create a temp array with key as id and values as array of child ids. Will use this array to process next direct childs. It would be a depth first traversal.
Also set id as key in input array, so we can directly access whole node when required.
function generateTree($data){
$arrChild = []; // Store parent as key and its childs as array to quickly find out.
foreach($data as $obj){
$arrChild[$obj->parent_id][] = $obj->id;
$data[$obj->id] = $obj;
}
$final = [];
$setChild = function(&$array, $parents) use (&$setChild, $data, $arrChild){
foreach($parents as $parent){
$temp = $data[$parent];
// If key is set, that means given node has direct childs, so process them too.
if(isset($arrChild[$parent])){
$temp->children = [];
$setChild($temp->children, $arrChild[$parent]);
}
$array[] = $temp;
}
};
// Empty key would represent nodes with parent as `null`
$setChild($final, $arrChild['']);
return $final;
}
$final = generateTree($arr);
echo json_encode($final, JSON_PRETTY_PRINT);
Output:
[
{
"id": 1,
"navigation_id": 4,
"parent_id": null,
"text": "link 1",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 2,
"navigation_id": 4,
"parent_id": 1,
"text": "link 2",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 3,
"navigation_id": 4,
"parent_id": 2,
"text": "link 3",
"icon": "fas fa-ad",
"url": "https:\/\/google.com",
"page_id": null
},
{
"id": 4,
"navigation_id": 4,
"parent_id": 2,
"text": "link 4",
"icon": "fab fa-google",
"url": "https:\/\/google.com",
"page_id": null
}
]
}
]
},
{
"id": 5,
"navigation_id": 4,
"parent_id": null,
"text": "link 5",
"icon": "",
"url": null,
"page_id": 5,
"children": [
{
"id": 6,
"navigation_id": 4,
"parent_id": 5,
"text": "link 6",
"icon": "",
"url": null,
"page_id": 4
},
{
"id": 7,
"navigation_id": 4,
"parent_id": 5,
"text": "link 7",
"icon": "",
"url": null,
"page_id": 4
}
]
}
]

How to convert a string inside an array to array and combine both together?

I have an array which look like this :
Result 1:
{
"error_code": 0,
"error_message": "",
"return_data": {
"items": [
{
"id": 462,
"users": "1,36,38"
},
{
"id": 462,
"users": "1,4"
},...... //same for 20 result
]
}
}
I want the users to convert to an array,and separate by the comma,so the whole result will look like this :
The result I want:
{
"error_code": 0,
"error_message": "",
"return_data": {
"items": [
{
"id": 462,
"users": [
{
"user_id": 1
},
{
"user_id": 36
},
{
"user_id": 38
}
],
}.. //other result
]
}
}
Here is what I try :
$res = "array the get the Result 1";
$items = //"I get the items array"
foreach ($items as $key => $item) {
$usersArray = array(); //create a new Array
//spilt the string to array separate with ","
$usersIdArray = explode(',', $items['users']);
//assign the "user_id" key to each value
foreach ($userIdArray as $key => $user) {
$usersArray['user_id'] = $user;
}
//assign the result back to $res
$res['return_data']['items']['users'] = $usersArray;
}
After I assign the array to $res with this line of code $res['return_data']['items']['users'] = $usersArray; ,my result become like below,I state the problem inside the code :
{
"error_code": 0,
"error_message": "",
"return_data": {
"items": {
"0":{ <-- // here suddenly appear a number for each result
"id": 462,
"users": "1,36,38" //here nothing change (I want the array appear here)
},
"1":{
"id": 462,
"users": "1,36,38"
},
"2":{
"id": 462,
"users": "1,36,38"
},
"users": { //the array appear here but not the position I want..and it only appears 1 time.
"user_id": "38"
}
}
}
}
So my question is,how can I convert a String in an array to an array,assign value to the key,and make it inside the array?
Somebody please help..Thanks
Like the comments above, you could have done it the first time so you wouldn't need another structure conversion, but anyway, your code is already there a bit, its just you need to create another nesting since you want another level:
So you need another level here:
"users": [
{
"user_id": 1
},
{
"user_id": 36
},
{
"user_id": 38
}
],
So in the code, just add []. This translates into:
foreach ($items as $key => $item) {
$usersArray['id'] = $item['id'];
$usersArray['users'] = array(); //create a new Array
$usersIdArray = explode(',', $item['users']);
foreach ($usersIdArray as $key => $user) {
$usersArray['users'][] = array('user_id' => $user); // push each batch of key pair "user_id" key and value "each exploded id"
// another level ^
}
$res['return_data']['items'][] = $usersArray;
}
Here's the fiddle to check it out.

Combine Two Multidimensional Arrays in PHP With The Same ID

I want to add values from one array to another that are both potentially multidimensional when they have the same ID. Hard to explain so I added example.
arr1 => new structure, without full data
arr2 => old structure, with full data
Examples if you'd like to help out:
// arr1 (the correct structure/order, without the full data)
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15"
}]
}]
}]
// arr2 (full data, not in any specific order, may be nested)
[{
"id": "24",
"name": " x",
"time": "0",
"status": "0"
}, {
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}]
// arr3 (desired output for this example)
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}],
"name": " x",
"time": "0",
"status": "0"
}]
I tried this:
function merge($arr1, $arr2) {
foreach($arr1 as $key => $value){
foreach($arr2 as $value2) {
if($value['id'] === $value2['id']){
$arr1[$key]['name'] = $value2['name'];
$arr1[$key]['time'] = $value2['time'];
$arr1[$key]['status'] = $value2['status'];
if (is_array($value)) {
$arr1[$key]['children'] = merge($arr1, $arr2);
}
}
}
}
return $arr1;
}
to combine them, but I can't figure out how to handle the nesting correctly. I have tried a lot of other things as well like using array_merge_recursive() but it doesn't work because I want to merge based on ID value. Any help on getting me on track would be awesome thanks.
Current output for example:
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15"
}]
}],
"name": " x",
"time": "0",
"status": "0"
}]
Desired output for example:
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}],
"name": " x",
"time": "0",
"status": "0"
}]
Edit: How about this?
$detailsClean = [];
foreach($array2 as $item) {
$detailsClean = removeDepth($item, $detailsClean);
}
foreach($array1 as $itemKey => $item) {
$array1[$itemKey] = addDetails($item, $detailsClean);
}
function removeDepth($array, $result) {
$id = $array['id'];
if (!empty($array['children'])) {
foreach($array['children'] as $child) {
$result = removeDepth($child, $result);
}
unset($array['children']);
}
$result[$id] = $array;
return $result;
}
function addDetails($array, $details) {
$id = $array['id'];
if (isset($details[$id])) {
$array = array_merge($array, $details[$id]);
if (!empty($array['children'])) {
foreach($array['children'] as $childKey => $child) {
$array['children'][$childKey] = addDetails($child, $details);
}
}
}
return $array;
}
$array1 is updated with the final result.
Here is an example with the data from your unedited post: http://phpio.net/s/7z09
EDIT - I think I understand the problem now
From the example you gave, I realised the problem - your old array has all the data, but not the parent-child relationships, so you want to populate the new array (with the correct relationships) with the data form the old array. The problem here is that the merge function would have to fetch data from an arbitrary generation in the old array to populate the new array. This could mean a lot of looping.
So I think the solution is first to loop through the old data and flatten it - just have an associative array where the key is the "id" value. Then go through the new array and populate it from the flattened, "lookup" array. Does that make sense? In any case, you'd have two functions:
$lookUp = array();
//recursive function to flatten $arr2 into $lookUp array.
function indexOriginal($arr, &$lookUp) {
foreach($arr as $value) {
$lookUp[$value["id"]] = $value;
if (isset($value['children'])) {
unset($lookUp[$value["id"]]['children']);
indexOriginal($value['children'], $lookUp);
}
}
}
indexOriginal($arr2, $lookUp);
Then you populate the new array:
function fillInNew($arr, $lookUp) {
$return = array();
foreach($arr as $value) {
$newEntry = $lookUp[$value["id"]];
if (isset($value['children'])) $newEntry['children'] = fillInNew($value['children'], $lookUp);
$return[] = $newEntry;
}
return $return;
}
$newArr = fillInNew($arr1, $lookUp);
And $newArr should be what you're looking for
OLD USELESS STUFF FROM BEFORE:
This part of your code is weird to me:
if (is_array($value)) {
$arr1[$key]['children'] = merge($arr1, $arr2);
}
Obviously I may be completely confused, but don't you just need to put this?
if (isset($value2['children'])) $arr1[$key]['children'] = array_merge($arr1[$key]['children'], $value2['children']);
EDIT: I added array_merge because I saw that the 'children' array in the incomplete version could also need merging.
EDIT 2: now I've noticed that children can have further children (makes sense, I guess), which is why you had the correct idea of using the function recursively. You just seem to have passed in the wrong arrays - you want to pass in $arr1[$key]['children'] (as the incomplete array) and $value2['children'] (as the complete array)
function merge($arr1, $arr2) {
foreach($arr1 as $key => $value){
foreach($arr2 as $value2) {
if($value['id'] === $value2['id']){
$arr1[$key]['name'] = $value2['name'];
$arr1[$key]['time'] = $value2['time'];
$arr1[$key]['status'] = $value2['status'];
if (isset($value2['children'])) $arr1[$key]['children'] = merge($arr1[$key]['children'], $value2['children']);
}
}
}
return $arr1;
}

Categories