My problem is that I want to create a getter which uses multidimensional array. I also use php version lower than 5.4 so I can't use array dereferencing.
class someClass{
protected someArray; // array( 'key1' => array( 'key2' => 'val'))
function __construct(){
// calling
$this -> getVar( array( 'key1' , 'key2' ) );
}
public function getVar( $keys ){
// return someArray multidimensional values
}
}
You mean something like this?
class someClass{
protected someArray;
function __construct(){
$this->someArray = array(
'key1' => array('name' => 'Akhil', 'loc' => 'india'),
'key2' => array('name' => 'Akash', 'loc' => 'usa'),
'key3' => array('name' => 'Dad', 'loc' => 'dubai'),
'key4' => array('name' => 'Mom', 'loc' => 'uae')
);
// calling
print_r( $this -> getVar( array( 'key1' , 'key2' ) ) );
}
public function getVar( $keys ){
// return someArray multidimensional values
$temp = array();
foreach($keys as $key)
{
$temp[] = $this->someArray[ $key ];
}
return $temp;
}
}
EDIT
As per the comment you provided below in my answer, here's how you could do that:
public function getVar( $main_key, $sub_key ){
// return someArray multidimensional values
return $this->someArray[ $main_key ][ $sub_key ];
}
Related
Here I have a function where I delete some keys from a nested array.
How could I optimise it in order to look prettier and maybe more efficiently?
$data is array type and contains the following:
$data = [
'0' => [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3'
],
'1' => [
'key1' => 'value4',
'key2' => 'value5',
'key3' => 'value6'
],
];
And $column(s) is of type array and contains keys that I want to remove from this array of arrays
$columns = ['key1', 'key2'];
And a part of the function written in PHP is:
***
$newData = [];
foreach ($data as $row) {
$newRow = array_diff_key($row,array_flip($columns));
array_push($newData, $newRow);
}
return $newData;
***
Expected result:
$data = [
'0' => [
'key1' => 'value1'
],
'1' => [
'key1' => 'value4'
],
];
The function works well but it looks awful :D
I hope this solves your problem
function delKey ($data, $columns){
$result = array();
for ($i = 0; $i<count($data); $i++){
$a = $data["$i"];
foreach ($columns as $key){
if (isset($a[$key])){
unset($a[$key]);
}
}
$result["$i"] = $a;
}
return $result;
}
Given a class with a multidimensional array as private property.
I'm trying to write a method setValue() which could change any value of this private array by passing:
some kind of path to a specific value (e.g. $path = ['lorem' => ['ipsum' => ['dolor' => ['sit' => null]]]];)
the new value
...
Class Tree {
private $tree = array(
'lorem' => array(
'ipsum' => array(
'dolor' => array(
'sit' => 'old value'
)
),
'amet' => array(
'consetetur' => array(
'sadipscing' => 'another value'
)
)
)
);
// setValue has to be a recursive function I guess
public function setValue($path, $value) {
// ???
}
public function getTree() {
return $this->tree;
}
}
Thanks to a comment of Gabriel on php.net I was able to find a working solution.
You can see my solution among the answers below.
What I actualy want to know: What are alternative approaches?
There are some recursive array built-in functions that may help you here:
<?php
class Tree
{
private $tree =
[
'foo' =>
[
'bar' => 'baz',
'qux' => 'quux'
]
];
public function replaceLeaf($key, $value) {
array_walk_recursive(
$this->tree,
function(&$item, $index) use ($key, $value) {
if($key === $index)
$item = $value;
}
);
}
public function replace($value) {
$this->tree = array_replace_recursive(
$this->tree,
$value
);
}
public function getTree()
{
return $this->tree;
}
}
$tree = new Tree;
$tree->replaceLeaf('bar', 'stool');
$tree->replace(
['foo' => ['qux' => 'quack']]
);
var_export($tree->getTree());
Output:
array (
'foo' =>
array (
'bar' => 'stool',
'qux' => 'quack',
),
)
However looking at your original question targeting the array is likely an easier syntax, especially if you are only changing one attribute at a time. You'd have to change the visibility of the array accordingly:
$tree->tree['foo']['qux'] = 'whatever';
With the decisive hint from gabriel.sobrinho#gmail.com at www.php.net
The method rebuildTree does the job. But it doesn't just change a value in $this->tree. It creates a new array.
Class Tree {
private $tree = array(
'lorem' => array(
'ipsum' => array(
'dolor' => array(
'sit' => 'old value'
)
),
'amet' => array(
'consetetur' => array(
'sadipscing' => 'another value'
)
)
)
);
public function setValue(array $path_and_value) {
$this->tree = $this->rebuildTree($this->tree, $path_and_value);
}
public function getTree() {
return $this->tree;
}
private function rebuildTree(array $arr, array $path_and_value) {
foreach($path_and_value AS $key => $value) {
if(
is_array($value)
&& isset($arr[$key])
) {
$arr[$key] = $this->rebuildTree($arr[$key], $value);
}
else {
$arr[$key] = $value;
}
}
return $arr;
}
}
$Tree = new Tree();
$path_and_value = array(
'lorem' => array(
'ipsum' => array(
'dolor' => array(
'sit' => 'new value'
)
)
)
);
$Tree->setValue($path_and_value);
print_r($Tree->getTree());
// ['lorem' => ['ipsum' => ['dolor' => ['sit' => 'new value']]]]
I want to combine 3 small arrays that have unique keys between them into 1 big array but when I modify a value in the big array I want it also to reflect in the corresponding small array.
For example I have these 3 small arrays:
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
I want to have a $bigArray that has each key's address linked/mapped to each value of the small arrays. So if I do something like:
$bigArray['key4'] = 'something else';
then it would modify $arr2['key4'] to the same value ('something else').
If I try something like:
$bigArray = [&$arr1, &$arr2, &$arr3];
It has the unfortunate effect of making a multidimensional array with the keys to the values mapped.
Two ways i found
<?php
error_reporting(E_ALL);
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
$big = [];
if (true) {
foreach (['arr1', 'arr2', 'arr3'] as $v) {
foreach (array_keys($$v) as $k) {
$big[$k] = &${$v}[$k];
}
}
}
else {
foreach ([&$arr1, &$arr2, &$arr3] as &$v) {
foreach (array_keys($v) as $k) {
$big[$k] = &$v[$k];
}
}
}
$big['key1'] = 'data1mod';
print_r($big);
print_r($arr1);
3rd way with function
$big = [];
$bindToBig = function (&$a) use (&$big) {
foreach (array_keys($a) as $k) {
$big[$k] = &$a[$k];
}
};
$bindToBig($arr1);
$bindToBig($arr2);
$bindToBig($arr3);
You can't bind data that way, but you can link them in the same object:
class ArrayLink {
public $bigArray;
public $linkedChildrenArray;
protected $childrenArray;
public function __construct( $childrenArray ) {
$this->childrenArray = $childrenArray;
}
public function changeValueForKey( $arrKey, $arrValue ) {
foreach ( $this->childrenArray as $key => $value ) {
foreach ( $value as $subKey => $subValue ) {
if ( $arrKey == $subKey ) {
$this->bigArray[ $subKey ] = $arrValue;
$this->childrenArray[ $key ][ $subKey ] = $arrValue;
}
}
}
$this->linkedChildrenArray = (object) $this->childrenArray;
}
}
As you can see, the $arr2 now need to be access from $arrayLink object:
$arr1 = [ 'key1' => 'data1', 'key2' => 'data2' ];
$arr2 = [ 'key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5' ];
$arr3 = [ 'key6' => 'data6' ];
$arrayLink = new ArrayLink( array( 'arr1' => $arr1, 'arr2' => $arr2, 'arr3' => $arr3 ) );
$arrayLink->changeValueForKey( 'key3', 'new value for key 3' );
echo $arrayLink->bigArray['key3']; //new value for key 3
echo $arrayLink->linkedChildrenArray->arr2['key3']; //new value for key 3
I have a multidimensional array with variable number of levels of data. That is, I can't be sure how many iterations it will take to reach the Goal level, which is an array. Something like this:
[
'key1' = 'value1',
'key2' = 'value2',
'key3' = [
'key4' => [
'key5' => 'value3'
],
'key6' => 'value4'
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7'
]
],
'key8' => 'value8'],
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10'
]
]
]
I've tried both array_walk_recursive and ArrayIterator, but neither seems to quite get me where I need to be.
I need to go through each element of the array, and if the key is Goal examine the value (eg. the array that Goal holds) and see if that array contains the value Foo.
If Foo is found in the array, I need to add a new value (in addition to Foo-- so call it Bar) to the array and then continue, since there may be more Goals in the parent array.
Is there a way to "stop" the iterator when we find a Goal, without iterating further, and then do the array_search operation?
Edit: Trying somethings along these lines--
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($iterator as $key => $value)
{
if($key == 'Goal')
{
if (is_array($value)) {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
}
}
}
Not entirely sure if this is what you want to achieve but here's a solution which adds Bar to arrays nested in the Goal key:
$array = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => [
'key4' => [
'key5' => 'value3',
],
'key6' => 'value4',
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7',
],
],
'key8' => 'value8',
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10',
],
],
];
function iterate(array $data, $goal = false)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
$value = iterate($value, $key === 'Goal');
} elseif (is_string($value)) {
if (($value === 'Foo') && $goal) {
$data[] = 'Bar';
return $data;
}
}
}
return $data;
}
var_export(iterate($array));
The code generates the following output:
array (
'key1' => 'value1',
'key2' => 'value2',
'key3' =>
array (
'key4' =>
array (
'key5' => 'value3',
),
'key6' => 'value4',
),
'key7' =>
array (
'Goal' =>
array (
0 => 'value5',
1 => 'value6',
2 => 'value7',
),
),
'key8' => 'value8',
'key9' =>
array (
'Goal' =>
array (
0 => 'value9',
1 => 'Foo',
2 => 'value10',
3 => 'Bar',
),
),
)
Iterators in my opinion would be weird to use in these kind of arrays... I would do it with something like this:
/*
Usage:
$wasFound = checkArray( "Goal", "Foo", $the_array);
if ( $wasFound ) echo "Key and Value pair found in the array!";
else { /* not found */ }
*/
function checkArray( $key_to_find, $value_to_find, $my_var, $last_key = NULL ) {
$found = FALSE;
if ( $last_key == $key_to_find && $my_var == $value_to_find )
return TRUE;
if ( $my_var == NULL )
return FALSE;
if ( is_array( $my_var ) ) {
foreach ( $my_var AS $key => $value )
{
if ( $found ) {
/* Do something else if needed when found */
break;
}
$found = checkArray( $key_to_find, $value_to_find, $value, $key );
}
}
return $found;
}
I agree that recursion is the way to do this. The problem with using array_walk_recursive is that you will not be able to see the Goal key, because as per the PHP documentation,
Any key that holds an array will not be passed to the function.
I am not really sure whether using a RecursiveIteratorIterator would be better than just writing a recursive function for it. A function for something like this should be fairly simple.
function addBar(&$array) {
foreach ($array as $key => &$value) {
if ($key === 'Goal') {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
} elseif (is_array($value)) {
addBar($value);
}
}
}
This function takes a reference to your array, so it will update your actual array rather than creating a copy with Bar added to each Goal.
Let's say I want to do this:
$a = array_intersect_assoc(
array(
'key1' => array(
'key2' => 'value2'
),
'key3' => 'value3',
'key4' => 'value4'
),
array(
'key1' => array(
'key2' => 'some value not in the first parameter'
),
'key3' => 'another value'
)
);
var_dump( $a );
The printed result is:
array
'key1' =>
array
'key2' => string 'value2' (length=6)
It's clear that values associated with 'key2' in both arrays are not the same, however array_intersect_assoc() still return 'key2' => 'value2' as the intersected value.
Is this the expected behavior of array_intersect_assoc()?
Thanks!
Yes, it's the expected behavior, because the comparison is done using string representations, and the function does not recurse down nested arrays. From the manual:
The two values from the key => value pairs are considered equal only if (string) $elem1 === (string) $elem2 . In other words a strict type check is executed so the string representation must be the same.
If you tried to intersect with an array with 'key1' => 'Array', you'd get the same result because the string representation of an array is always 'Array'.
One of the user-contributed notes, by nleippe, contains a recursive implementation that looks promising (I modified the third line to do string comparison on any non-array values):
function array_intersect_assoc_recursive(&$arr1, &$arr2) {
if (!is_array($arr1) || !is_array($arr2)) {
// return $arr1 == $arr2; // Original line
return (string) $arr1 == (string) $arr2;
}
$commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
$ret = array();
foreach ($commonkeys as $key) {
$ret[$key] =& array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);
}
return $ret;
}
function array_key_match_recursive(array $main, array $other, $i = 0, &$result = []) {
foreach($main as $key => $value) {
$k = sprintf('%s%s', str_repeat('=', $i), $key);
if (!isset($other[$key])) {
$result[$k][] = 'not key';
}
if (!is_array($value) && empty($other[$key])) {
$result[$k][] = 'value empty';
}
if (is_array($value) && isset($other[$key])) {
array_key_match_recursive($value, $other[$key], ++$i, $result);
}
}
//return (bool) !$result;
return $result;
}
A function that does what you need:
/**
* Get array intersect assoc recursive.
*
* #param mixed $value1
* #param mixed $value2
*
* #return array|bool
*/
function getArrayIntersectAssocRecursive(&$value1, &$value2)
{
if (!is_array($value1) || !is_array($value1)) {
return $value1 === $value2;
}
$intersectKeys = array_intersect(array_keys($value1), array_keys($value2));
$intersectValues = [];
foreach ($intersectKeys as $key) {
if (getArrayIntersectAssocRecursive($value1[$key], $value2[$key])) {
$intersectValues[$key] = $value1[$key];
}
}
return $intersectValues;
}
My version based on #BoltClock version:
function array_intersect_assoc_recursive($arr1, $arr2) {
if (!is_array($arr1) || !is_array($arr2)) {
return $arr1;
}
$commonkeys = array_keys($arr1);
if (!array_key_exists('$', $arr2)){
$commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
}
$ret = array();
foreach ($commonkeys as $key) {
$ret[$key] = array_intersect_assoc_recursive($arr1[$key], array_key_exists('$', $arr2) ? $arr2['$'] : $arr2[$key]);
}
return $ret;
}
I use this code to filter data inside complex array
example:
$filter = [
'channels' => [
'$' => [
'id' => 1,
'type' => 1,
'count' => 1
]
],
'user' => [
'id' => 1,
'type' => 1
]
];
$data = [
'user' => [
'id' => '1234',
'type' => true,
'counter' => 14,
],
'filteredField' => 4,
'channels' => [
['id' => '567', 'type' => 'other', 'count' => 1345, 'filteredField' => 5,],
['id' => '890', 'type' => 'other', 'count' => 5456, 'filteredField' => 7,],
],
];
print_r(array_intersect_assoc_recursive($data, $filter));
test online:
https://onlinephp.io/c/3be04