navigate through huge nested array in PHP - php

This might be a simple question, but I am dumping an object ($this) which is absolutely huge and I need to get to a specific point in the array
$this->varA->varB->varC->varD->what_I_need
I know that the variable that I need is in there and I can use ctrl+f to find it, but the array is so nested that I don't know how I should get to it in PHP. Any ideas on what the best way is to do this?

Do not hesitate to look at libraries from frameworks.
[CakePHP] made an awesome class which is able to navigate into arrays using a string in dot syntax notation. This library is known as Hash, just look at it.
If you have this :
$var = array(
'Element1' => array(
'First_rule' => true,
'Second_rule' => false,
),
'Element2' => array(
'First_rule' => 'none',
'Other_rule' => 'otherone',
),
);
You can extract datas from this array simply with a string.
You can take only one information from a specific element :
$extracted_other_rule = Hash::extract($var, 'Element2.Other_rule');
Returns :
Array
(
[0] => otherone
)
Or you can even extract all "First_rule" indexes from any element in the array containing it :
$extracted_rules = Hash::extract($var, '{s}.First_rule');
Returns this :
Array
(
[0] => 1
[1] => none
)
If you need to navigate through a huge array with undefined depth, just make a recursive function, transferring a string named "$path" to recursive actions. I made a big function of this kind to parse a whole XML stream into a JSON string with all keys parsed with my own rules, with an array of parameters.

Related

Return array value

I've got a bit of a complicated array that I'm working with and I'm not sure how to retrieve one of the values in the array. The array structure is as below and I'm trying to get the 'value'.
array (
0 => Drupal\search_api\Query\Condition::__set_state(
array(
'field' => 'title',
'value' => 'hello',
'operator' => 'CONTAINS',
)
),
)
I tried using $array[0]['value'] but it returns NULL.
Any ideas on how to go about this one?
The Drupal\search_api\Query\Condition::__set_state there is how var_export tells you that there is an object in $array[0]. The array shown inside that is a view of the internal data of that object, which may not be directly accessible at all.
You need to find the documentation or source code for the Drupal\search_api\Query\Condition class and see how it provides access to its data. For instance, the access might look like this (it probably doesn't, this is an example not a suggestion):
echo $array[0]->tellMeTheValue();

PHP custom DataType for multidimensional arrays

I am working with multidimensional, associative arrays a lot, mainly for configuration data and introduced "breadcrumbs" into the use of multidimensional arrays.
The basic idea: my classes are designed to handle one-dimensional (flat) breadcrumb-arrays as a reference-guide for certain values inside a multidimensional, associative array. Every incremented index of a breadcrumb is basically a level deeper inside the array until the last level and associative key are found and reached in recursion. E.g.:
$myArray = array(
'firstLevel' => array(
'secondLevel' => array(
'myValue' => 42
),
'anotherLevel' => array(
'anotherValue' => 13
)
)
);
$myBreadcrumb = array('firstLevel', 'secondLevel', 'myValue');
$fancyClass = new \someNamespace\fancyArrayProcessingClass($myArray);
$myValue = $fancyClass->getValueForBreadcrumb($myBreadcrumb);
If it's requested i'll post an example for the processing of the breadcrumbs, too, but since i'm targeting on custom data types i found it being off-topic.
It is getting tiresome and is overhead to always code "Wrapper" classes, implemented classes or another kind of construct to make arrays navigable via breadcrumb. I wondered if there is a way to introduce real new DataTypes into PHP that can be handled like actual DataTypes. My idea of a good syntax for this concept:
$myArray = navigableArray(
'firstLevel' => array(
'secondLevel' => array(
'myValue' => 42
),
'anotherLevel' => array(
'anotherValue' => 13
)
)
);
$myBreadcrumb = array('firstLevel', 'secondLevel', 'myValue');
$myValue = $myArray[$myBreadcrumb];
Or even more intuitive to use with just xpath-style strings:
$myArray = navigableArray(
'firstLevel' => array(
'secondLevel' => array(
'myValue' => 42
),
'anotherLevel' => array(
'anotherValue' => 13
)
)
);
$myValue = $myArray['firstLevel/secondLevel/myValue'];
I know that there is a sentence in the PHP documentation that says something like "developers will never need to introduce their own DataTypes into PHP", but AFAIK there is no reason given why it is like that and why a developer is - unlike with almost every other language - unable to introduce fully custom DataTypes.
Edit:
For anyone curious: i found an alternative route, with the standard php class "ArrayAccess" you can make your PHP Object behave like an actual array. The famous "Judy" class incorporates "ArrayAccess" and "Iterator" and fits exactly what i was looking for in this question-thread.
http://php.net/manual/de/class.arrayaccess.php
http://php.net/manual/de/class.judy.php
Based on your use-case, I'd recommend you look into PHP's SPL Iterators. Specifically the Recursive Iterator interface or the RecursiveArrayIterator implementation. These Iterators are built natively into PHP, are very fast, and many allow you to access them using native PHP functions such as foreach(), for(), count(), etc.
You can then extend one of the SPL Iterator interfaces to create your own custom class that gives you functionality specific to your Breadcrumb needs. FWIW, I suppose it's true that developers should not have to create their own native data types in PHP (because you can't) but creating custom types with PHP classes is highly encouraged over using native arrays.

Check, if array A contains all items from array B, when both arrays are multidimensional

I want to check, if array A contains all the items from array B (may contain others, but must contain all), when both arrays are multidimensional, i.e. can contains different variable types.
I've seen a lot (particularly this, this, this, this, this and this, also this, this and this as well). I've read PHP doc. Everything, that I checked, fails with "Array to string conversion" notice. Especially wen using array_intersect() or array_diff().
I'm using strict error checking, so notices actually holds further execution of entire script and are something, I don't generally like and want to avoid. Is it possible in this case?
My array A is:
Array
(
[0] => content/manage/index
[Content] => Array
(
[title] =>
[type] => 5
[category] =>
[recommended] =>
[featured] =>
[status] =>
[views] =>
[last_access_date] =>
[creation_date] =>
[modification_date] =>
[availability_date] =>
[author_id] =>
)
)
My array B is:
Array
(
[0] => /content/manage/index
[Content] => Array
(
[type] => 1
)
)
So, is there any way I can if I can use array_intersect on multidimensional arrays containing different variable types without getting notice?
My problem (and question) came out of misunderstanding, what "Array to string conversion" notice really means. In my case, it was trying to tell me, that I'm trying to walk multidimensional array with functions designed to be used on single dimension array.
Understanding that led me to a solution within few seconds. There are many of them here, on SO, but the one given by deceze here looked the best for me. So I adopted it into the form of such function:
function recursiveArrayIntersect($array1, $array2)
{
$array1 = array_intersect_key($array1, $array2);
foreach($array1 as $key=>&$value)
{
if(is_array($value)) $value = recursiveArrayIntersect($value, $array2[$key]);
}
return $array1;
}
I adopted it to my project and my way of coding, but all the credits still goes to deceze (his answer here)!
Now I can find an intersection of virtually any array, no matter what kind of variable types it contain and no matter of, how deep it is (how many subarrays it contains).

Why does the sort order of multidimensional child arrays revert as soon as foreach loop used for sorting ends?

I have a very strange array sorting related problem in PHP that is driving me completely crazy. I have googled for hours, and still NOTHING indicates that other people have this problem, or that this should happen to begin with, so a solution to this mystery would be GREATLY appreciated!
To describe the problem/question in as few words as possible: When sorting an array based on values inside a multiple levels deeply nested array, using a foreach loop, the resulting array sort order reverts as soon as execution leaves the loop, even though it works fine inside the loop. Why is this, and how do I work around it?
Here is sample code for my problem, which should hopefully be a little more clear than the sentence above:
$top_level_array = array('key_1' => array('sub_array' => array('sub_sub_array_1' => array(1),
'sub_sub_array_2' => array(3),
'sub_sub_array_3' => array(2)
)
)
);
function mycmp($arr_1, $arr_2)
{
if ($arr_1[0] == $arr_2[0])
{
return 0;
}
return ($arr_1[0] < $arr_2[0]) ? -1 : 1;
}
foreach($top_level_array as $current_top_level_member)
{
//This loop will only have one iteration, but never mind that...
print("Inside loop before sort operation:\n\n");
print_r($current_top_level_member['sub_array']);
uasort($current_top_level_member['sub_array'], 'mycmp');
print("\nInside loop after sort operation:\n\n");
print_r($current_top_level_member['sub_array']);
}
print("\nOutside of loop (i.e. after all sort operations finished):\n\n");
print_r($top_level_array);
The output of this is as follows:
Inside loop before sort operation:
Array
(
[sub_sub_array_1] => Array
(
[0] => 1
)
[sub_sub_array_2] => Array
(
[0] => 3
)
[sub_sub_array_3] => Array
(
[0] => 2
)
)
Inside loop after sort operation:
Array
(
[sub_sub_array_1] => Array
(
[0] => 1
)
[sub_sub_array_3] => Array
(
[0] => 2
)
[sub_sub_array_2] => Array
(
[0] => 3
)
)
Outside of loop (i.e. after all sort operations finished):
Array
(
[key_1] => Array
(
[sub_array] => Array
(
[sub_sub_array_1] => Array
(
[0] => 1
)
[sub_sub_array_2] => Array
(
[0] => 3
)
[sub_sub_array_3] => Array
(
[0] => 2
)
)
)
)
As you can see, the sort order is "wrong" (i.e. not ordered by the desired value in the innermost array) before the sort operation inside the loop (as expected), then is becomes "correct" after the sort operation inside the loop (as expected).
So far so good.
But THEN, once we're outside the loop again, all of a sudden the order has reverted to its original state, as if the sort loop didn't execute at all?!?
How come this happens, and how will I ever be able to sort this array in the desired way then?
I was under the impression that neither foreach loops nor the uasort() function operated on separate instances of the items in question (but rather on references, i.e. in place), but the result above seems to indicate otherwise? And if so, how will I ever be able to perform the desired sort operation?
(and WHY doesn't anyone else than me on the entire internet seem to have this problem?)
PS.
Never mind the reason behind the design of the strange array to be sorted in this example, it is of course only a simplified PoC of a real problem in much more complex code.
Your problem is a misunderstanding of how PHP provides your "value" in the foreach construct.
foreach($top_level_array as $current_top_level_member)
The variable $current_top_level_member is a copy of the value in the array, not a reference to inside the $top_level_array. Therefore all your work happens on the copy and is discarded after the loop completes. (Actually it is in the $current_top_level_member variable, but $top_level_array never sees the changes.)
You want a reference instead:
foreach($top_level_array as $key => $value)
{
$current_top_level_member =& $top_level_array[$key];
EDIT:
You can also use the foreach by reference notation (hat tip to air4x) to avoid the extra assignment. Note that if you are working with an array of Objects, they are already passed by reference.
foreach($top_level_array as &$current_top_level_member)
To answer you question as to why PHP defaults to a copy instead of a reference, it's simply because of the rules of the language. Scalar values and arrays are assigned by value, unless the & prefix is used, and objects are always assigned by reference (as of PHP 5). And that is likely due to a general consensus that it's generally better to work with copies of everything expect objects. BUT--it is not slow like you might expect. PHP uses a lazy copy called copy on write, where it is really a read-only reference. On the first write, the copy is made.
PHP uses a lazy-copy mechanism (also called copy-on-write) that does
not actually create a copy of a variable until it is modified.
Source: http://www.thedeveloperday.com/php-lazy-copy/
You can add & before $current_top_level_member and use it as reference to the variable in the original array. Then you would be making changes to the original array.
foreach ($top_level_array as &$current_top_level_member) {

Create associative array from an array without keys

Edit (Some background):
I am dealing with some data for states/regions/etc within a country. The data looks like so (essentially translated to english and its original form in the local language):
Array
(
['Anhui'] => "安徽省",
...etc
)
Now that's all fine and well if I am displaying the data to someone from China. However, if the visitor is from another country, it is clearly better to display the english name (Anhui). This is essentially the point of this exercise.
The data is then used to generate a drop down to set the value and options.
I have an array that looks like so:
Array
(
[0] => "item1"
[1] => "item2"
[2] => "item3"
)
I would like to turn this into an associate array:
Array
(
["item1"] => "item1"
["item2"] => "item2"
["item3"] => "item3"
)
Are there any ways to do this besides using a foreach loop?
Just use the same variable for both parameters of array_combine():
$arr = array_combine($arr, $arr);
However, I don't really see the point of this. Perhaps you could explain what you are trying to accomplish, so you could find the best solution.
You can use some php array functions
I think this would work, even though there are other ways.
$new_array = array_combine(array_values($old_array),$old_array);

Categories