I have array of arrays - tree structure of main menu.
I try to find in this tree one node with needed slug and return this node with all it's childs.
I write little recurcive function
<?php
$tree = '[{"id":1,"structure":1,"parent":0,"slug":"medicinskaya-ge","child":{"2":{"id":2,"structure":1.1,"parent":1,"slug":"dnk-diagnostika","child":{"3":{"id":3,"structure":"1.1.1","parent":2,"slug":"dnk-diagnostika","datafile":"ssz","template":"ssz"},"4":{"id":4,"structure":"1.1.2","parent":2,"slug":"dnk-diagnostika","child":{"5":{"id":5,"structure":"1.1.2.1","parent":4,"slug":"dnk-diagnostika"},"6":{"id":6,"structure":"1.1.2.2","parent":4,"slug":"testirovanie-ge"},"7":{"id":7,"structure":"1.1.2.3","parent":4,"slug":"dnk-diagnostika"}}},"8":{"id":8,"structure":"1.1.3","parent":2,"slug":"dnk-diagnostika"},"9":{"id":9,"structure":"1.1.4","parent":2,"slug":"texnologiya-kol"}}}}}]';
$tree = json_decode($tree, true);
function getSlugData(string $slug, array $data)
{
foreach ($data as $row) {
if ($row['slug'] == $slug) {
return $row;
}
if (isset($row['child'])) {
//return $this->getSlugData($slug, $row['child']);
}
}
return [];
}
$result = getSlugData('testirovanie-ge', $tree);
print_r($result);
But as a result I have an empty array. If I print_r($row) when $row['slug'] == $slug - It appears on screen.
if ($row['slug'] == $slug) {
exit(print_r($row));
return $row;
}
What's my mistake?
In programming, recursion is a useful and powerful mechanism that allows a function to call itself directly or indirectly, that is, a function is said to be recursive if it contains at least one explicit or implicit call to itself.
I modified your code a bit and got the solution below.
$tree = '[{"id":1,"structure":1,"parent":0,"slug":"medicinskaya-ge","child":{"2":{"id":2,"structure":1.1,"parent":1,"slug":"dnk-diagnostika","child":{"3":{"id":3,"structure":"1.1.1","parent":2,"slug":"dnk-diagnostika","datafile":"ssz","template":"ssz"},"4":{"id":4,"structure":"1.1.2","parent":2,"slug":"dnk-diagnostika","child":{"5":{"id":5,"structure":"1.1.2.1","parent":4,"slug":"dnk-diagnostika"},"6":{"id":6,"structure":"1.1.2.2","parent":4,"slug":"testirovanie-ge"},"7":{"id":7,"structure":"1.1.2.3","parent":4,"slug":"dnk-diagnostika"}}},"8":{"id":8,"structure":"1.1.3","parent":2,"slug":"dnk-diagnostika"},"9":{"id":9,"structure":"1.1.4","parent":2,"slug":"texnologiya-kol"}}}}}]';
$tree = json_decode($tree, true);
//print_r($tree);
//die();
function getSlugData(string $slug, array $data, string $key = 'slug')
{
$result = [];
foreach ($data as $row) {
// Checks if the key exists and is the desired value for that key in the array
if (isset($row[$key]) && $row[$key] === $slug) {
return $row;
}
// If it is an array, apply recursion by calling getSlugData again
if (is_array($row)) {
$result = getSlugData($slug, $row, $key);
if ($result !== []) {
return $result;
}
}
}
return $result;
}
print_r(getSlugData('testirovanie-ge', $tree));
print_r(getSlugData('texnologiya-kol', $tree));
print_r(getSlugData('nothing-here', $tree));
die();
In the recursive function, you must not break the loop early unless you find your slug match. If a non-slug-match has a child element, you must iterate it and potentially pass up a match in subsequent recursive calls.
Code: (Demo)
function getSlugData(string $slug, array $data): array
{
foreach ($data as $row) {
if ($row['slug'] === $slug) {
return $row;
}
if (isset($row['child'])) {
$deeper = getSlugData($slug, $row['child']);
if ($deeper) {
return $deeper;
}
}
}
return [];
}
P.s. you aren't calling a class method, so $this-> is inappropriate.
Related
The model Person has has-many relation to PersonType:
public function getPersonTypes()
{
return $this->hasMany(PersonType::className(), ['PersonID' => 'PersonID']);
}
I need to show in view all PersonType's related values.
Model Person:
public function ListPersonTypes()
{
$data = $this->getPersonTypes()->all();
$list = array();
foreach ($data as $value) {
$list[] = $value['PersonTypeName'];
return implode(', ', $list);
}
}
But, why ListPersonTypes() returns only first row from table PersonType?
public function ListPersonTypes()
{
$data=$this->getPersonTypes;
$list=array();
foreach ($data as $value){
$list[]=$value['PersonTypeName'];
return implode(',',$list);
}
}
Should do the trick.
you don't use it correctly $this->getPersonTypes()->all(); use
this $this->personTypes
write the return out of the loop
Yii2 (gii) by default create this functions for you access that like a attribute of model and return as arrray .
This function hasnt be creates to access as a function literal .
Try this code
public function ListPersonTypes()
{
// this go to function getPersonTypes and return a array of them
$data=$this->personTypes;
$list=array();
foreach ($data as $value){
$list[]=$value['PersonTypeName'];
}
return implode(',',$list);
}
Seems that in your code you are running just one iteration... as you call a return inside the foreach.
public function ListPersonTypes()
{
$data = $this->getPersonTypes()->all();
$list = array();
foreach ($data as $value) {
$list[] = $value['PersonTypeName'];
}
return implode(', ', $list);
}
How to refactor this snippet of code, to reduce indentation level by one?
I just wonder is it possible in PHP to write this code in a diffrent way, with just one level of indentation.
The code:
private function isArrayMatchingCriteria(array $array) {
foreach($array as $element) {
if (! $this->isElementMatchingCriteria($element) {
return false;
}
}
return true;
}
Please take into consideration, that:
this code doesn't always iterate over all array elements - so combination of count + array_filter / array_map isn't the same
it is easy to do by introducing a dedicated object attribute serving as a flag, but I'm looking for a way without introducing new attributes
If you're just looking to remove indentation, you could use:
private function isArrayMatchingCriteria(array $array) {
foreach($array as $element) {
if (!$this->isElementMatchingCriteria($element)) return false;
}
return true;
}
Use array_map, something like this:
class MyClass
{
private function isElementMatchingCriteria( $element )
{
// DUMMY, replace with actual code
if ( $element == "foo" || $element == "bar" ) {
return true;
} else {
return false;
}
} // end is Element Matching Criteria
public function isArrayMatchingCriteria(array $array)
{
$results = array_map( array( $this, "isElementMatchingCriteria"), $array );
$isMatch = true;
foreach ( $results as $result ) {
$isMatch = $isMatch && $result;
} // end foreach
return $isMatch;
} // end function isArrayMatchingCriteria
} // end MyClass
$myClass = new MyClass();
$array = array( "foo", "bar", "baz" );
$result = $myClass->isArrayMatchingCriteria( $array );
print_r( $result );
I have created a recursive function which returns the array value. So I am calling that function from another function. but it doesn't return any values. The function is given below,
public function sitemapAction() {
$sites = self::getNavigation();
foreach(self::recursiveSitemap($sites) as $url) {
...
...
}
}
public function recursiveSitemap($sites)
{
$result = array();
foreach ($sites as $site) {
$result[]= array($site['DFA']);
if (is_array($site['items'])) {
return recursiveSitemap($site['items']);
}
}
}
Please help me on this.
If you look carefully at your recursive method, you will see that it actually does not return anything at all:
public function recursiveSitemap($sites)
{
$result = array();
foreach ($sites as $site) {
$result[]= array($site['DFA']);
if (is_array($site['items'])) {
return recursiveSitemap($site['items']);
}
}
}
If your variable $site['items'] is an array you call your method again, going deeper, without doing anything with the returned value.
And if not?
It would seem you would need to add the result you get back from your recursive function call to your $result array and return that, but I don't know exactly what output you expect.
Apart from that you have a small typo, you need self::recursiveSitemap($site['items']) if you want to call the same method recursively.
A simple example:
public function recursiveSitemap($sites)
{
$result = array();
foreach ($sites as $site) {
if (is_array($site['items'])) {
// do something with the result you get back
$result[] = self::recursiveSitemap($site['items']);
} else {
// not an array, this is the result you need?
$result[]= array($site['DFA']);
}
}
// return your result
return $result;
}
Assuming that $result is the thing you want to get back...
What is the php implementation of underscore's _.findWhere({key:'val'})?
This is how the method is documented by Underscorejs.org:
_.findWhere(list, properties)
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
I wrote these little functions. Might be handy.
function where($list, $props)
{
$result = array_filter(
$list,
function ($e) use ($props)
{
$count = 0;
foreach ($props as $key => $value)
{
if ($value == $e[$key])
{
$count += 1;
}
return $count == count($props);
}
}
);
return $result;
}
function findWhere($list, $props)
{
$result = where($list, $props);
return array_values($result)[0];
}
There is none. Underscore is a collection of functions written in JS. It has nothing to do with PHP.
How can I iterate a multidimensional array and filter on string nodes? I'm trying to create an easy way to sanitize data coming into my application via POST and think this would be really handy.
You can use a recursive function to traverse the array and filter its string components. For example:
function doFilter($arr) {
foreach ($arr as $key => $value) {
if (is_string($value)) {
$arr[$Key] = sanitize($value);
} else if (is_array($value)) {
$arr[$key] = doFilter($value);
}
}
return $arr;
}
function sanitize($str) {
// Perform necessary steps to sanitize $str
return $str;
}
You need to use Recursion
function sanitizePost($post) {
if (is_array($post)){
foreach ($post as $k => $p) {
$post[$k] = sanitizePost($p);
}
} else {
$post = sanatizeStringFunction($post); // may be use regex or something
}
return $post;
}