PHP multidimensional array search by value with random sub-level array - php

Is it possible to search a multidimensional array of unknown depth by value?
For example, with:
$data = [
[
'uid' => '100',
'name' => 'MAIN',
[
'uid' => '2222',
'name' => 'SUB_MAIN',
[
'uid' => '8524',
'name' => 'SUB_SUB_MAIN',
]
]
],
[
'uid' => '5465',
'name' => 'MAIN',
],
[
'uid' => '40489',
'name' => 'MAIN',
]
];
I want to find the path to the sub-array where uid is 8524.
With the above array, the result should be: [0, 0, 0].

You can use the following as a starting point:
<?php
declare(strict_types=1);
error_reporting(-1);
ini_set('display_errors', 'On');
function findPath(array $items, callable $criteria, array $path = []): array {
foreach ($items as $key => $item) {
if (!is_array($item)) {
continue;
}
// prepare path to this item
$pathToHere = array_merge($path, [$key]);
// if the items fits the criteria
if ($criteria($item)) {
// return it's path
return $pathToHere;
}
// otherwise check children
$pathToChild = findPath($item, $criteria, $pathToHere);
// and if return value is not empty
if (count($pathToChild) > 0) {
// return path to child
return $pathToChild;
}
}
// base case if no item matches
return [];
}
$data = [
[
'uid' => '5465',
'name' => 'MAIN',
],
[
'uid' => '100',
'name' => 'MAIN',
[
'uid' => '2222',
'name' => 'SUB_MAIN',
[
'uid' => '8524',
'name' => 'SUB_SUB_MAIN',
[
'uid' => 'X',
'name' => 'Y',
]
],
[
'uid' => '8524_test',
'name' => 'SUB_SUB_MAIN_test',
[
'uid' => '8524_test_sub',
'name' => 'SUB_SUB_MAIN_test_sub',
]
]
]
],
[
'uid' => '40489',
'name' => 'MAIN',
]
];
$path = findPath($data, fn(array $item): bool => $item['uid'] === '8524_test_sub');
print_r($path);
/*
Array
(
[0] => 1
[1] => 0
[2] => 1
[3] => 0
)
*/
Demo: https://3v4l.org/XKTH8
Note the code above requires php 7.4, but only because of the fn() => ... construct. This could simply be replaced by any other suitable callable.

Related

Change array keys to a value within the array

Edit:
Trying to avoid doing a loop outside of the $data array you see. As I need to do this a couple of times and it looks messy.
I have got an array similar to this:
$links = [
[
'type_id' => '1',
'url' => ''
],
[
'type_id' => '2',
'url' => ''
]
];
$types = [
[
'id' => 1,
'value' => 'facebook'
],
[
'id' => 2
'value' => 'twitter'
]
];
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => $links
]
];
I need the keys within my $data['primary']['social_links'] to use the $type['value'] rather than just being 0, 1, etc...
So $data would look like:
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => [
'facebook' => [
'type_id' => '1',
'url' => ''
],
'twitter' => [
'type_id' => '2',
'url' => ''
]
]
]
];
Is there a way I can do this with array_map or something?
You can just use array_combine with the output of the value column of $types (generated using array_column) as keys and the values from $links:
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => array_combine(array_column($types, 'value'), $links)
]
];
print_r($data);
Output:
Array (
[primary] => Array (
[address_details] => Array ( )
[contact_details] => Array ( )
[social_links] => Array (
[facebook] => Array (
[type_id] => 1
[url] =>
)
[twitter] => Array (
[type_id] => 2
[url] =>
)
)
)
)
Demo on 3v4l.org
Update
Based on the edit to OPs question, things get a lot more complicated to give a one-line solution. This should work:
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => array_map(function ($v) use ($links) { return $links[array_search($v, array_column($links, 'type_id'))]; }, array_column($types, 'id', 'value'))
]
];
Demo on 3v4l.org
A simple for loop can do it:
https://3v4l.org/h47MG
<?php
$links = [
[
'type_id' => '1',
'url' => ''
],
[
'type_id' => '2',
'url' => ''
]
];
$types = [
[
'value' => 'facebook'
],
[
'value' => 'twitter'
]
];
$result = [];
for($i = 0, $len = count($types); $i < $len; $i++) {
$result[$types[$i]['value']] = $links[$i];
}
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => $result
]
];
var_dump($data);
You could use a loop to modify the array directly :
for($i=0; $i < sizeof($links); $i++) {
$links[$types[$i]['value']] = $links[$i];
unset($links[$i]);
}
var_dump($links);
Output :
["facebook"]=> array(2) {
["type_id"]=> string(1) "1"
["url"]=> string(0) ""
}
["twitter"]=> array(2) {
["type_id"]=> string(1) "2"
["url"]=> string(0) ""
}
Or by using array_combine if you do not want a loop, per your comment on another answer :
array_combine(array_column($types, 'value'), $links)
<pre><code>
$social_links = [];
foreach ($types as $type):
$social_links[$type['value']] = $links[$key];
endforeach;
$data = [
'primary' => [
'address_details' => [],
'contact_details' => [],
'social_links' => $social_links
]
];
print_r($data);
</code></pre>

Sort multi-dimensional array so specific sub-array is first

I want to sort an array so that a specific array with a specific value is shown as the first in the array.
The array I have:
array = [
[0] => [
'id' => 123,
'name' => 'Random'
],
[1] => [
'id' => 156,
'name' => 'keyboard'
],
[2] => [
'id' => 12235,
'name' => 'Text'
],
];
I want the sub-array where the name is 'keyboard' to be the first in line of the big array.
Does anyone have suggestions?
usort Sort an array by values using a user-defined comparison function
$array = [
0 => [
'id' => 123,
'name' => 'Random'
],
1 => [
'id' => 156,
'name' => 'keyboard'
],
2 => [
'id' => 12235,
'name' => 'Text'
],
];
usort($array, function ($item) {
return $item['name'] != 'keyboard';
});
print_r($array);
See the demo
$myArray = [
[0] => [
'id' => 123,
'name' => 'Random'
],
[1] => [
'id' => 156,
'name' => 'keyboard'
],
[2] => [
'id' => 12235,
'name' => 'Text'
],
];
$temp = $myArray[0];
$myArray[0] = $myArray[1];
$myArray[1] = $temp;

How can run recursive function on multiple Level array in php

I have two arrays one is $apiRes and second is $mappData i want to match fields exist in mappData array and assign value of apiRes to match field.
Note: response of api may have different and mapdata array will change according to api response My two array and output format :
<?php
$apiRes = [
[
'firstname' => 'first name des',
'title' => "title des",
'category' => 1,
'result' =>
[
0 => [
'name' => 'Masterpass',
'skill' => 'low level one'
],
1 => [
'name' => 'Visa',
'skill' => 'low level two'
],
2 => [
'name' => 'Pocketpos',
'skill' => 'low level three'
],
],
'list' => [
'product_name'=>'product name',
'amount' => [
'currency'=>'$',
'kind' => 'kind'
]
],
'priority' => 'Low',
'visible_to' => 'Everyone',
]
];
$mappData = [
0 => [
"src_field" => "firstname",
"target_field" => "new1519110449758",
"src_field_data_type" => "string"
],
1 => [
"src_field" => "result.name",
"target_field" => "new1519110811942",
"src_field_data_type" => "string"
],
2 => [
"src_field" => "list.product_name",
"target_field" => "new1519110451708",
"src_field_data_type" => "string"
],
3 => [
"src_field" => "list.amount.currency",
"target_field" => "new1517556165360",
"src_field_data_type" => "string"
]
];
My final output should be:
$output = [
"new1519110449758" => "first name des",
"new1519110451708" => "product name",
"new1517556165360" => "$",
"new1519110811942" => [
0 => "Masterpass",
1 => "Visa",
2 => "Pocketpos"
]
];
Please help
Thanks
I've changed your mappData slightly as it's difficult to know how to deal with the result.name element as it's repeated. What I've done is change it so that it's *result.name and it uses the fact there is an * in it to mean there are multiple values.
I've tried to comment the code with enough to explain each bit, the main principle is using a recursive routine to go through each level of the array one step at a time.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$apiRes = [
[
'firstname' => 'first name des',
'title' => "title des",
'category' => 1,
'result' =>
[
0 => [
'name' => 'Masterpass',
'skill' => 'low level one'
],
1 => [
'name' => 'Visa',
'skill' => 'low level two'
],
2 => [
'name' => 'Pocketpos',
'skill' => 'low level three'
],
],
'list' => [
'product_name'=>'product name',
'amount' => [
'currency'=>'$',
'kind' => 'kind'
]
],
'priority' => 'Low',
'visible_to' => 'Everyone',
]
];
$mappData = [
0 => [
"src_field" => "firstname",
"target_field" => "new1519110449758",
"src_field_data_type" => "string"
]
,
1 => [
"src_field" => "*result.name",
"target_field" => "new1519110811942",
"src_field_data_type" => "string"
],
2 => [
"src_field" => "list.product_name",
"target_field" => "new1519110451708",
"src_field_data_type" => "string"
],
3 => [
"src_field" => "list.amount.currency",
"target_field" => "new1517556165360",
"src_field_data_type" => "string"
]
];
$result = [];
// Process next element of the array
function getArrayElement ( $next, $data, $array = false ) {
// Extract key for this level
$key = array_shift($next);
// If starts with * then this means there are multiple of them
if ( $key[0] == "*" ){
$nextArray = true;
// remove from current key
$key = substr($key,1);
}
else {
$nextArray = false;
}
if ( $array ){
$res = [];
// extract the data from each element at this level
foreach ( $data as $read ) {
$res[] = $read[$key];
}
$data = $res;
}
else {
// Fetch the element for the key for this level
$data = $data [ $key ];
}
// If there are more levels to deal with then repeat this method
if ( count($next) > 0 ) {
$data = getArrayElement ( $next, $data, $nextArray );
}
return $data;
}
// Flatten out original array if necessary
if ( count($apiRes) == 1 ){
$apiRes = $apiRes[0];
}
// Process each part of lookup
foreach ( $mappData as $element ) {
// Create an array of the elments broken down into each level
$map = explode( '.', $element['src_field']);
$result[$element['target_field']] = getArrayElement($map, $apiRes);
}
print_r($result);

Need to push the key and value inside associative Array?

I need to push the more key and its value inside the array. If I use below code first key pair replaced by 2nd one.
For your Reference:
Code Used:
foreach ($projectData['projectsections'] as $key => $name) {
$projectData['projectsections'][$key] = ['name' => $name];
$projectData['projectsections'][$key]= ['id' => '1'];
}
Current result:
'projectsections' => [
(int) 0 => [
'id' => '1'
],
(int) 1 => [
'id' => '1'
]
],
Expected:
'projectsections' => [
(int) 0 => [
'name' => 'test1',
'id' => '1'
],
(int) 1 => [
'name' => 'test2',
'id' => '1'
]
],
How can I build this array in PHP?? Any one help??
You need to either add the entire array:
$projectData['projectsections'][$key] = ['name' => $name, 'id' => '1'];
Or add with the key name:
$projectData['projectsections'][$key]['name'] = $name;
$projectData['projectsections'][$key]['id'] = '1';
With
$projectData['projectsections'][$key] = ['name' => $name];
$projectData['projectsections'][$key]= ['id' => '1'];
you are setting a new Array for that $key. This is not what you want.
This should work:
$projectData['projectsections'][$key] = ['name' => $name, 'id' => '1'];
Change it to :
foreach ($projectData['projectsections'] as $key => $name) {
$projectData['projectsections'][$key]['name'] = $name;
$projectData['projectsections'][$key]['id'] = '1';
}

Recursively Create an Array from another Array

I am trying to make a multi-dimensional array build an array path adding the hr field so it looks like this:
I just can't figure out how to add the totals, nor create a sub-array so the dot notation in an option too. My goal is to get something like this:
[1] => [1][2][1][5][0][6] = 35 (the second child path "1")
[1] => [1][2][1][5][0][7] = 25
or Something like this:
array (
[children.0.children.0.children.0.total] = 20
[children.0.children.1.children.1.total] = 35
// etc
)
The complicated part is that it goes in different directions and I want to know what is the highest and lowest total based on the path:
==> Run Code Here or Copy/Paste
// -------------
// The Flattener
// -------------
function doit($myArray) {
$iter = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($iter as $leafKey => $leafValue) {
$keys = array();
foreach (range(0, $iter->getDepth()) as $depth) {
$keys[] = $iter->getSubIterator($depth)->key();
}
$result[ join('.', $keys) ] = $leafValue;
}
return $result;
}
// -------------
// Example Tree
// -------------
$tree = [
'id' => 1,
'type' => 'note',
'data' => [],
'children' => [
[
'id' => 2,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 3,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
'id' => 4,
'type' => 'exit',
'data' => [],
'children' => []
]
],
[
'id' => 5,
'type' => 'note',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 6,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => []
],
[
'id' => 7,
'type' => 'exit',
'data' => [],
'children' => []
],
]
]
],
]
]
];
$result = doit($tree);
print_r($result);
This seems to work, I found it somewhere googling all day.
array_reduce(array_reverse($keys), function($parent_array, $key) {
return $parent_array ? [$key => $parent_array] : [$key];
}, null);

Categories