This question already has answers here:
How to access and manipulate multi-dimensional array by key names / path?
(10 answers)
Closed 2 years ago.
I'm trying to get reach a point in a dynamicly generated multidimensional array based on a array with keys.
Basicly I have the following array:
$arr = [
"something" => [
'something_else' => [
"another_thing" => "boo"
]
],
"something2" => [
'something_elseghf' => [
"another_thingfg" => [
"hi" => "bye"
]
]
],
"info" => [
'something_else2' => [
"another_thingh" => "boo"
]
],
];
Now I want to set a value in the array based on the keys in a different array:
$keyArr = ["something2", 'something_elseghf' "another_thingfg", "hi"];
So the above array means that I need to set the hi key to some value. How can I reach that part of the array with these random keys, note that the length of $keyArr is dynamic aswell. So I can't reach it with:
$arr[$keyArr[0]][$keyArr[1]][$keyArr[2]][$keyArr[3]] =
Hope anyone has an idea on how to solve this!
Try this approach:
$arr = [
"something" => [
'something_else' => [
"another_thing" => "boo"
]
],
"something2" => [
'something_elseghf' => [
"another_thingfg" => [
"hi" => "bye"
]
]
],
"info" => [
'something_else2' => [
"another_thingh" => "boo"
]
],
];
$keyArr = ["something2", 'something_elseghf', "another_thingfg", "hi"];
$cursor = $arr;
foreach ($keyArr as $key) {
$cursor = $cursor[$key];
}
echo $cursor;
Will echo
bye
UPDATE:
If you want to change a value within multi-dimentional array, then use a recursive function, like this:
function changeValue($array, $path, $value) {
if (empty($path)) {
return $value;
}
$key = array_shift($path);
$array[$key] = changeValue($array[$key], $path, $value);
return $array;
}
$arr = [
"something" => [
'something_else' => [
"another_thing" => "boo"
]
],
"something2" => [
'something_elseghf' => [
"another_thingfg" => [
"hi" => "bye"
]
]
],
"info" => [
'something_else2' => [
"another_thingh" => "boo"
]
],
];
$keyArr = ["something2", 'something_elseghf', "another_thingfg", "hi"];
$changedArray = changeValue($arr, $keyArr, 'New value!');
print_r($changedArray);
Will output
Array
(
[something] => Array
(
[something_else] => Array
(
[another_thing] => boo
)
)
[something2] => Array
(
[something_elseghf] => Array
(
[another_thingfg] => Array
(
[hi] => New value!
)
)
)
[info] => Array
(
[something_else2] => Array
(
[another_thingh] => boo
)
)
)
Related
What is the best method to remove commas from numbers in the associative array below? Keep the commas in text, thanks.
$main_arr contains the following 3 arrays:
Array
(
[phrase] => Hi, I'm ok
[number_a] => 3,575
[number_b] => 64
[number_c] => 8,075
)
Array
(
[phrase] => Bye, it's late
[number_a] => 7,365
[number_b] => 32
[number_c] => 648,120
)
Array
(
[phrase] => Good catch!
[number_a] => 11,659
[number_b] => 128
[number_c] => 1,492,352
<?php
$mainArray =[
[
'phrase' => "Hi, I'm ok",
'number_a' => "3,575",
'number_b' => "64",
'number_c' => "8,075",
],
[
'phrase' => "Bye, it's late",
'number_a' => "7,365",
'number_b' => "32",
'number_c' => "648,120",
],
[
'phrase' => 'Good catch!',
'number_a' => "11,659",
'number_b' => "128",
'number_c' => "1,492,352",
],
];
foreach($mainArray as &$array) {
foreach($array as &$val) {
if (preg_match('/^[0-9,]*$/', $val)){
$val = str_replace(',','',$val);
}
}
}
var_export($mainArray);
I have an array with fields
[
"house" => "30|30|30",
"street" => "first|second|third",
...
]
I want to get array
[
[
"house" => "30",
"street" => "first",
...
],
[
"house" => "30",
"street" => "second",
...
],
[
"house" => "30",
"street" => "third",
...
]
]
I know how I can solve this using PHP and loop, but maybe this problem has more beautiful solution
use zip
$data = [
"house" => "30|30|30",
"street" => "first|second|third",
];
$house = collect(explode('|',$data['house']));
$street = collect(explode('|',$data['street']));
$out = $house->zip($street);
$out->toarray();
Here's something I managed to do with tinker.
$original = [
"house" => "30|30|30",
"street" => "first|second|third",
];
$new = []; // technically not needed. data_set will instantiate the variable if it doesn't exist.
foreach ($original as $field => $values) {
foreach (explode('|', $values) as $index => $value) {
data_set($new, "$index.$field", $value);
}
}
/* dump($new)
[
[
"house" => "30",
"street" => "first",
],
[
"house" => "30",
"street" => "second",
],
[
"house" => "30",
"street" => "third",
],
]
*/
I tried using collections, but the main problem is the original array's length is not equal to the resulting array's length, so map operations don't really work. I suppose you can still use each though.
$new = []; // Since $new is used inside a Closure, it must be declared.
collect([
"house" => "30|30|30",
"street" => "first|second|third",
...
])
->map(fn($i) => collect(explode('|', $i))
->each(function ($values, $field) use (&$new) {
$values->each(function ($value, $index) use ($field, &$new) {
data_set($new, "$index.$field", $value);
});
});
$array = ["house" => "30|30|30","street" => "first |second| third"];
foreach($array as $key=> $values){
$explodeval = explode('|',$values);
for($i=0; $i<count($explodeval); $i++){
$newarray[$i][$key]= $explodeval[$i];
}
}
output:
Array
(
[0] => Array
(
[house] => 30
[street] => first
)
[1] => Array
(
[house] => 30
[street] => second
)
[2] => Array
(
[house] => 30
[street] => third
)
)
I would like to merge two arrays to compare old vs new values. For example, $arr1 is old values $arr2 is new values.
In case when the data is deleted $arr2 is an empty array. Example:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [];
For this my expected output after merge is
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => null
],
"name" => [
'old' => "DB1",
'new' => null
],
"slug" => [
'old' => "db1",
'new' => null
],
"url" => [
'old' => "https://www.db1.org",
'new' => null
],
]
]
];
if arr2 is different then the values should be present in the new field instead of null.
For example:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [
"databases" => [
0 => [
"id" => 5
"name" => "DB2"
"slug" => "db2"
"url" => "https://www.db2.com"
]
]
];
expected output:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => 5
],
"name" => [
'old' => "DB1",
'new' => "DB2"
],
"slug" => [
'old' => "db1",
'new' => "db2"
],
"url" => [
'old' => "https://www.db1.org",
'new' => "https://www.db2.com"
],
]
]
];
Case 3 is when $arr1 is empty but $arr2 is populated:
$arr1 = [];
$arr2 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
and the expected output is:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => null,
'new' => 1
],
"name" => [
'old' => null,
'new' => "DB1"
],
"slug" => [
'old' => null,
'new' => "db1"
],
"url" => [
'old' => null,
'new' => "https://www.db1.org"
],
]
]
];
The inbuilt php functions cannot format the data in old vs new format so was wondering how to go about this? Any solutions/suggestions would be appreciated.
Update
Here is what I had tried before:
I had tried simple array_merge_recursive but it does not store the source array. So if you have $arr1 key not there, the final merged array will only have one value.
I tried some more recursive functions late in the night but failed so in essence didn't have anything to show for what I had tried. However, this morning, I came up with the solution and have posted it as an answer in case anyone needs to use it.
Interesting question, as long as a (non-empty) array on one side means to traverse into it and any skalar or null is a terminating node (while if any of old or new being an array would enforce traversing deeper so dropping the other non-array value):
It works by mapping both old and new on one array recursively and when the decision is to make to traverse to offer null values in case a keyed member is not available while iterating over the super set of the keys of both while null would represent no keys:
$keys = array_unique(array_merge(array_keys($old ?? []), array_keys($new ?? [])));
$merged = [];
foreach ($keys as $key) {
$merged['old'] = $old[$key] ?? null;
$merged['new'] = $new[$key] ?? null;
}
This then can be applied recursively, for which I found it is easier to handle both $old and $new as ['old' => $old, 'new' => $new] for that as then the same structure can be recursively merged:
function old_and_new(array $old = null, array $new = null): array
{
$pair = get_defined_vars();
$map =
static fn(callable $map, array $arrays): array => in_array(true, array_map('is_array', $arrays), true)
&& ($parameter = array_combine($k = array_keys($arrays), $k))
&& ($keys = array_keys(array_flip(array_merge(...array_values(array_map('array_keys', array_filter($arrays, 'is_array'))))))
)
? array_map(
static fn($key) => $map($map, array_map(static fn($p) => $arrays[$p][$key] ?? null, $parameter)),
array_combine($keys, $keys)
)
: $arrays;
return $map($map, $pair);
}
print_r(old_and_new(new: $arr2));
Online demo: https://3v4l.org/4KdLs#v8.0.9
The inner technically works with more than two arrays, e.g. three. And it "moves" the array keys upwards, similar to a transpose operation. Which btw. there is a similar question (but only similar, for the interesting part in context of your question it is not answered and my answer here doesn't apply there directly):
Transposing multidimensional arrays in PHP
After reviewing my own code here is the solution I came up with. I am posting it here in case someone else needs a solution for this:
/**
* Function to merge old and new values to create one array with all entries
*
* #param array $old
* #param array $new
* #return void
*/
function recursiveMergeOldNew($old, $new) {
$merged = array();
$array_keys = array_keys($old) + array_keys($new);
if($array_keys) {
foreach($array_keys as $key) {
$oldChildArray = [];
$newChildArray = [];
if(isset($old[$key])) {
if(!is_array($old[$key])) {
$merged[$key]['old'] = $old[$key];
} else {
$oldChildArray = $old[$key];
}
} else {
$merged[$key]['old'] = null;
}
if(isset($new[$key])) {
if( !is_array($new[$key])) {
$merged[$key]['new'] = $new[$key];
} else {
$newChildArray = $new[$key];
}
} else {
$merged[$key]['new'] = null;
}
if($oldChildArray || $newChildArray) {
$merged[$key] = recursiveMergeOldNew($oldChildArray, $newChildArray);
}
}
}
return $merged;
}
Note - this solution needs testing.
This question already has answers here:
PHP multidimensional array search by value
(23 answers)
Closed 5 years ago.
Alright, so I'm querying the DB and generating an array from a list of IP addresses:
$q = 'SELECT ip FROM proxy';
$r = mysqli_fetch_all($con->query($q), MYSQLI_ASSOC);
Array returned looks like this:
Array
(
[0] => Array
(
[ip] => 1.202.244.222
)
[1] => Array
(
[ip] => 1.226.238.136
)
[2] => Array
(
[ip] => 1.228.231.247
)
[3] => Array
(
[ip] => 1.238.106.137
)
[4] => Array
(
[ip] => 1.238.155.191
)
But if I want to find say the first or any IP in the above list, for some reason it doesn't find anything:
$ip = "1.202.244.222";
if(in_array($ip,$r)) {
echo "gotcha";
}
What am I doing wrong here?
Got confused by the array within array stuff which I didn't notice at first.
Thanks to Zeth's pointers, I got it to work by collapsing the arrays into one by adding:
$r0 = array_column($r, 'ip');
And then:
if(in_array($ip,$r0)) {
echo "gotcha";
}
It's an array of arrays... Collapse the thing, and then it'll work. There are a couple of options here: How to "flatten" a multi-dimensional array to simple one in PHP?
The most flexible approach for such situations is to use a user defined comparison function:
<?php
$needle = '1.202.244.222';
$haystack = [
[
'ip' => '1.202.244.222'
],
[
'ip' => '1.226.238.136'
],
[
'ip' => '1.228.231.247'
],
[
'ip' => '1.238.106.137'
],
[
'ip' => '1.238.155.191'
]
];
$result = array_filter($haystack, function($entry) use ($needle) {
return isset($entry['ip']) && $needle === $entry['ip'];
});
print_r($result);
The output of above code obviously is:
Array
(
[0] => Array
(
[ip] => 1.202.244.222
)
)
Your Array condition was wrong.
$ip_find = '1.202.244.222';
$ip_values = [
[
'ip' => '1.202.244.222'
],
[
'ip' => '1.226.238.136'
],
[
'ip' => '1.228.231.247'
],
[
'ip' => '1.238.106.137'
],
[
'ip' => '1.238.155.191'
]
];
foreach ($ip_values as $key => $value) {
foreach ($value as $key => $ip) {
if ($ip==$ip_find) {
echo $ip." Gocha";
break;
}
}
}
You can do it using foreach:
$r = [
[
'ip' => '1.202.244.222'
],
[
'ip' => '1.226.238.136'
],
[
'ip' => '1.228.231.247'
],
[
'ip' => '1.238.106.137'
],
[
'ip' => '1.238.155.191'
]
];
$ip = "1.202.244.222";
foreach($r as $elem)
{
if($elem['ip'] == $ip)
{
echo "gotcha";
break;
}
}
I am looping through instances of associative arrays (these associative arrays themselves are part of an array).
For each array I want to return a value, based on a key.
Currently I have:
$image_path = array_column($myarray, 'uri');
But of course array_column stores its values in a array, which, considering it's only returning 1 value, is useless to me.
Is there an existing function that will allow me to just get a value based off a supplied key?
Eg:
$image_path = get_keys_value($myarray, 'uri');
Example array. This is a very basic example. The real thing has many levels to it:
$myarray = array
(
'instance' => array(
'type' => 'somedata',
'content' => somedata',
'image' => array(
'name' => 'photo',
'uri' => 'path/to/file.png'
),
),
);
Desired outcome:
$image_path contains 'path/to/file.png' string.
Try this,
function array_column_recursive(array $haystack, $needle)
{
$found = [];
array_walk_recursive($haystack, function ($value, $key) use (&$found, $needle) {
if ($key == $needle) {
$found[] = $value;
}
});
return $found;
}
echo array_column_recursive($myarray, 'uri')[0];
Here is working code.
array_column will work with only 2 level array structure.
Above array will solve your problem.
I hope this will help
I guess you can use array_map.
Ex:
$arr = [
[
'root' => [
'child1' => [
'child2' => 123
]
]
],
[
'root' => [
'child1' => [
'child2' => 456
]
]
],
[
'root' => [
'child1' => [
'child2' => 789
]
]
],
[
'root' => [
'child1' => [
'child2' => 123
]
]
],
];
print_r(array_map(function($row) {
// here goes expression to get required path
return $row['root']['child1']['child2'];
}, $arr));
Output:
Array
(
[0] => 123
[1] => 456
[2] => 789
[3] => 123
)