recursive php function: trouble spotting the bug - php

I have a family tree app in laravel, and I want to be able to show an outline view (start with a family from long ago, show its kids, show those kids' families, those families' kids, etc).
So I made this recursive get_descendants function:
public static function get_descendants(Family $family, $results_array, $counter)
{
// start new round with a different temp array, to keep track
$counter++;
$this_array = "array_$counter";
$$this_array = [];
array_push ($$this_array, $family->caption);
$kids = FamilyController::get_kids_of_family($family);
// if family has no kids, return 0;
if (!count($kids))
{
return 0;
}
else // add kids and check for their families
{
foreach ($kids as $kid) {
array_push ($$this_array, $kid->firstname);
// get families made by kid- for each one, call get_descendants
$families_made = FamilyController::get_families_person_made($kid);
foreach ($families_made as $new_family) {
array_push($$this_array, self::get_descendants($new_family, $$this_array, $counter));
}
};
// we've gone through the kids, add this round's array to the general results array
array_push ($results_array, $$this_array);
}
return $results_array;
}
I've confirmed with print statements that the looping through is correct, but there's a problem with the way I'm saving the results. I want to get something like this, where the top family shows once, with children and their families nested:
array:1 [▼
0 => array:4 [▼
0 => "Padme & Anakin"
1 => "Leia"
2 => array:3 [▼
0 => "Leia & Han"
1 => "Kylo Ren"
]
3 => "Luke"
]
]
but I'm getting this (with an extra repeat in the middle):
array:1 [▼
0 => array:4 [▼
0 => "Padme & Anakin"
1 => "Leia"
2 => array:3 [▼
0 => "Padme & Anakin"
1 => "Leia"
2 => array:3 [▼
0 => "Leia & Han"
1 => "Kylo Ren"
]
]
3 => "Luke"
]
]
Can anyone see where my mistake is?

Update: it turns out that it works if I get rid of that final results_array and just use the dynamic one the whole way, like this:
public static function get_descendants(Family $family, $results_array, $counter)
{
// start new round with a different temp array, to keep track
$counter++;
$this_array = "array_$counter";
$$this_array = [];
array_push ($$this_array, $family->caption);
$kids = FamilyController::get_kids_of_family($family);
// if family has no kids, return 0;
if (!count($kids))
{
return 0;
}
else // add kids and check for their families
{
foreach ($kids as $kid) {
array_push ($$this_array, $kid->first);
// get families made by kid- for each one, call get_descendants
$families_made = FamilyController::get_families_person_made($kid);
if (count($families_made))
{
foreach ($families_made as $new_family) {
array_push($$this_array, self::get_descendants($new_family, $$this_array, $counter));
}
}
};
}
return $$this_array;
}

Looks unnecessarily complex, with dynamic arrays, and you keep populating the same array, so that's why Luke appears in the wrong place.
A cleaner solution might be to be very specific about where people are in the array rather than using a dynamic array name. Just a suggestion -
public static function getDescendants(Family $family)
{
$family = [];
$family['name'] = $family->caption;
if ($kids = static::getKidsOfFamily($family)) {
foreach ($kids as $kid) {
$family['children'][] = $kid->firstname;
$subfamilies = static::getFamiliesPersonMade($kid);
foreach ($subfamilies as $subfamily) {
$family['subfamilies'][] = static::getDescendants($subfamily);
}
};
}
return $family;
}
would produce something like
array [
"name" => "Padme & Anakin"
"children" => array [
"Leia",
"Luke"
],
"subfamilies" => array [
array [
"name" => "Leia & Han"
"children" => array [
"Kylo Re"
]
]
]
]

Related

elements in array is not working with in_array

number 3 should be in array $songsid but when i used in_array method output was null
nuder my function is dd($songsid)
public function index()
{
$songs = Song::latest()->paginate(20);
$songid = Collection::select("song_id")->where("user_id", "=", auth::user()->id)->get();
$songsid = $songid->toArray();
// dd($songsid);
if(in_array(3, $songsid))
{
dd("yes");
}
else
{
dd("no");
}
}
output of $songsid array dd($songsid)
array:2 [▼
0 => array:1 [▼
"song_id" => 3
]
1 => array:1 [▼
"song_id" => 2
]
]
$songsid is not an array of scalar values, but an array of arrays.
You could use array_column() to extract IDs from the array,
in_array(3, array_column($songsid, 'song_id')
Unless you really need to convert to array, you could use collection methods to check that out.
$songid = Collection::select("song_id")->where("user_id", "=", auth::user()->id)->get();
if ($songId->contains('song_id', 3)) ...
In your in_array method, you need an indexed array, ex : $songsid = [3,2]; You can use pluck method :
$songsid = $songid->pluck('id')->toArray();
if(in_array(3, $songsid))
{
dd("yes");
} else {
dd("no");
}
in_array() searches in the array values, that is not nesting.
You may want to use array_culumn() like
in_array(3, array_column($songsid, 'song_id')
or search for ['song_id' => 3] like
in_array(3, array_column($songsid, ['song_id' => 3])

Compare arays and exclude element form one if it has duplicates in others

I need to compare arrays, if element from first or second array has duplicates in another one I need to exclude it. I know it sound simply and I'm sure it is but i cant handle with that problem :(
So i have first array like this:
Array:3 [
6 => blog/something
4 => blog/somethingElse
5 => blog/else
]
Second array almost identical:
Array:3 [
1 => /blog
2 => /comments
3 => /posts
]
And the last array:
(integer on the left is id of elements in second array, in this example
comments and posts)
Array:2 [
0 => array:2 [
'page_id' => 2
'value' => noindex
]
1 => array:2 [
'page_id' => 3
'value' => noindex
]
]
So if I have element in array first or second which exist in array thrid too AND have value = noindex i need to exclude it.
I have tried do this by foreach recursive, by array_walk_recursive but I still can't get satisfied result
First get all the indices you need to exclude and then exclude them:
$excludeIndices = array_column(array_filter($array3, function ($entry) {
return $entry['value'] === 'noindex';
}), 'page_id');
$keepArray1 = array_diff_key($array1, array_flip($excludeIndices));
$keepArray2 = array_diff_key($array2, array_flip($excludeIndices));
Sandbox
You can filter using the first two arrays directly.
$result = array_filter($last, function($item) use ($first, $second) {
return !($item['value'] == 'noindex' &&
(isset($first[$item['page_id']]) || isset($second[$item['page_id']]))
);
});

Recursive method fill array php laravel

I'm trying to create a recursive method to fill an array with each new item found.
function getUserUbigeoString($ubigeo_id){
$ubigeoRepository = new \App\Repositories\UbigeoRepository();
$ubigeos = array();
$ubigeo = $ubigeoRepository->getUbigeo($ubigeo_id);
if(!empty($ubigeo)){
if($ubigeo->ubigeo_id == null){
$ubigeos[] = $ubigeo->name;
}
$ubigeos[] = getUserUbigeoString($ubigeo->ubigeo_id);
}
return $ubigeos;
}
The objective of the code is to get an array fill with all the name of the ubigeos.
0 => ubigeo1
1 => ubigeo2
2 => ubigeo3
etc...
As of right now, i have tried placing the return many different locations, but the closest result i have gotten was:
array:1 [▼
0 => array:1 [▼
0 => array:2 [▼
0 => "Port Dusty"
1 => []
]
]
]
==========EDIT============
Structure of database ubigeos:
id name level ubigeo_id
----------------------------
3 ubigeo1 1 null
37 ubigeo2 2 3
55 ubigeo3 3 37
the output would be a simple array like so, which then i could implode into a comma separated string:
array:1 [
0 => 'ubigeo1'
1 => 'ubigeo2'
2 => 'ubigeo3'
]
so assuming that you really want to call this with function with an Ubigeo instance and only get the names from that and from the parent Ubigeo instances (i.e. calling the function with id 55 initially to get the result array), you can try something like this (I didn't want to modify your function call parameters - normally I would include the array as a function parameter instead of instantiating new one in each recursion step):
function getUserUbigeoString($ubigeo_id)
{
$ubigeoRepository = new \App\Repositories\UbigeoRepository();
$ubigeos = array();
$ubigeo = $ubigeoRepository->getUbigeo($ubigeo_id);
if(!empty($ubigeo))
{
if($ubigeo->ubigeo_id != null) {
$ubigeos = getUserUbigeoString($ubigeo->ubigeo_id);
}
$ubigeos[] = $ubigeo->name;
}
return $ubigeos;
}
Use Can do it with lists method in laravel
Ex :
$ubigeoRepository->lists('ubigeo_id','id')->all();

php Laravel - find element in array

I want to use foreach to showing orderIds in my output.
Here is my code :
$orders = $results['result']['data'];
foreach ($orders as $key => $order)
{
dd($order[$order]['orderId']);
}
here is $orders result :
1 => array:3 [
"orderId" => 4
"orderTotalPrice" => 100
}
"resId" => 1
]
2 => array:3 [
"orderId" => 18
"orderTotalPrice" => 100
}
"resId" => 1
]
3 => array:3 [
"orderId" => 34
"orderTotalPrice" => 100
}
"resId" => 1
]
4 => array:3 [
"orderId" => 64
"orderTotalPrice" => 100
}
"resId" => 1
]
Any suggestion?
By using foreach you can do this:
$orderIDs = [];
foreach ($orders as $order){
$orderIDs[] = $order['orderId'];
}
Or you can use the pluck method. It will retrieve all of the collection values for a given key:
collect($orders)->pluck('orderId')->all();
Maybe something like this?
$order_ids = [];
foreach ($orders as $order)
{
array_push($order_ids, $order['orderId']);
}
return $order_ids;
As per #IsmailRBOUH's answer, use his solution if you're adding/pulling x-handful of data. If you're looping over a heavy amount of data, would be better (performance wise) to use array_push. But honestly, it's a fractional difference between them both. $array[] usually landing on top...
PHP's website:
Note: If you use array_push() to add one element to the array it's
better to use $array[] = because in that way there is no overhead of
calling a function.
"For showing orderId's in your output:"
$orders = $results['result']['data'];
foreach ($orders as $key => $order)
{
echo $order['orderId'];
}

PHP array loop and format

I am very new to PHP, learning fast but not fast enough! I am also learning Laravel 5.1.
I am trying to build a HTML select list array from an Eloquent query output, in the correct format for form builder (Form::select).
I have the following call to Eloquent to pull the data:
// Get list of States for address select
$states = State::all()->toArray();
It returns the following array:
array:8 [▼
0 => array:2 [▼
"id" => "1"
"state" => "ACT"
]
1 => array:2 [▼
"id" => "2"
"state" => "NSW"
]
...
];
I want to loop through it and generate the following output:
array = [
'' => 'State', <-- This is the default for the select list
'1' => 'ACT',
'2' => 'NSW',
...
];
I am using Laravel 5.1, so I am using the included array_add() function in my helper.
I call my function like this:
$states = create_select_list($states, 'State');
I next want to format the output so it is ready for the Form::select statement. I have tried the code below (as the final try from a few iterations!) but unsuccessfully.
function create_select_list($data, $default)
{
// Declare array and set up default select item
$container = ['' => $default];
// Loop through data entries and build select list array
foreach($data as list($entry, list($key, $value))) {
$container = array_add($container, $key, $value);
}
// Return the select list array
return $container;
}
All help or suggestions are appreciated!
This answer is not about loop fix. I think previous comment should help you.
Just another idea. You can try use array_map instead foreach for this case.
For example:
$states = ['' => 'State'];
array_map(function($item) use (&$states) {
$states[$item['id']] = $item['state'];
}, State::all()->toArray());
Loop like below:
foreach($data as $key => $keyArr ) {
$container = array_add($container, $keyArr['id'], $keyArr['state']);
}
You don't need to use list() in your foreach loop, instead try:
foreach($data as $key => $value) {
$container = array_add($container, $key, $value);
}
The PHP documentation gives a good overview of what list() actually does.

Categories