Convert associative multidimensional array into single-dimensional array with treepath keys [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
PHP convert nested array to single array while concatenating keys?
Get array's key recursively and create underscore seperated string
Please, read the whole question before answering.
I have this multidimensional array:
$data = array(
'user' => array(
'email' => 'user#example.com',
'name' => 'Super User',
'address' => array(
'billing' => 'Street 1',
'delivery' => 'Street 2'
)
),
'post' => 'Hello, World!'
);
I want it flatten, transformed into:
$data = array(
'user.email' => 'user#example.com',
'user.name' => 'Super User',
'user.address.billing' => 'Street 1',
'user.address.delivery' => 'Street 2',
'post' => 'Hello, World!'
);
Important:
The keys are very important to me. I want them concatenated, separated by periods.
It should work with any level of nesting.
Thank you!

Something like this should work:
function flatten($array, $prefix = '') {
$result = array();
foreach($array as $key=>$value) {
if(is_array($value)) {
$result = $result + flatten($value, $prefix . $key . '.');
}
else {
$result[$prefix . $key] = $value;
}
}
return $result;
}
DEMO

Thanks for all the given answers.
I have transformed it in the following, which is an improved version. It eliminates the need of a root prefix, does not need to use references, it is cleaner to read, and it has a better name:
function array_flat($array, $prefix = '')
{
$result = array();
foreach ($array as $key => $value)
{
$new_key = $prefix . (empty($prefix) ? '' : '.') . $key;
if (is_array($value))
{
$result = array_merge($result, array_flat($value, $new_key));
}
else
{
$result[$new_key] = $value;
}
}
return $result;
}

Try this
<?php
$data = array(
'user' => array(
'email' => 'user#example.com',
'name' => 'Super User',
'address' => array(
'billing' => 'Street 1',
'delivery' => 'Street 2'
)
),
'post' => 'Hello, World!'
);
function prefixKey($prefix, $array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
$result = array_merge($result, prefixKey($prefix . $key . '.', $value));
else
$result[$prefix . $key] = $value;
}
return $result;
}
var_dump(prefixKey('', $data));
?>
Outputs
array
'user.email' => string 'user#example.com' (length=16)
'user.name' => string 'Super User' (length=10)
'user.address.billing' => string 'Street 1' (length=8)
'user.address.delivery' => string 'Street 2' (length=8)
'post' => string 'Hello, World!' (length=13)

test this out here
i passed by reference so no need for returns. just hand over the array storage.
$store = array();
function flatten($array,&$storage,$parentKey = ''){
foreach($array as $key => $value){
$itemKey = (($parentKey)? $parentKey.'.':'').$key;
if(is_array($value)){
flatten($value,$storage,$itemKey);
} else {
$storage[$itemKey] = $value;
}
}
}
flatten($data,$store);
var_dump($store);

Use recursion such as this:
function process_data( $data, $parent_key ){
if ( ! is_array( $data ) ){
return $data;
}
$flattened_array = array();
foreach( $data as $key => $item ){
$flattened_key = $parent_key . '.' . $key;
$flattened_array[ $flattened_key ] = process_data( $item, $flattened_key );
}
return $flattened_array;
}

Related

PHP remove array if subarray empty

my array image just like this, if subarray "name" is empty or null i want delete array, how to do that ?
here my current script
$data = array();
$fixedData = array();
$countyName = array();
$numrow = 2;
echo "<pre>";
// insert to tb participant => 1
foreach($sheet as $key => $row){
$data[] = array(
'name' => $this->split_name($row['B']),
'phone' => $row['D'],
'mobile' => $row['E'],
'institution' => $row['F'],
'departement' => $row['G'],
'address' => $row['H'],
'country' => $row['I'],
);
$numrow++;
}
unset($data[0]); //delete first row
$data = array_values($data);
//loop search data
var_dump ($data);
die();
Assume that you have the following data set,
$array = [
[
'name' => 'not null', 'phone' => 12546
],[
'name' => '', 'phone' => 852147
],[
'name' => null, 'phone' => 96325874
],[
'name' => 'have value', 'phone' => 12546
],
];
You can filter the nulled or empty values like several ways :
1-
foreach ($array as $key => &$value) {
if (empty($value['name']) || is_null($value['name'])) {
$value = null;
}
}
$array = array_filter($array);
2-
$newData = [];
foreach ($array as $key => $value) {
if (!empty($value['name']) && !is_null($value['name'])) {
$newData[] = $value;
}
}
3- using array_walk
$newData = [];
array_walk($array, function ($value, $key) use (&$newData) {
if (!empty($value['name']) && !is_null($value['name'])) {
$newData[] = $value;
}
});
4- using array_filter
$newData = array_filter($array, function ($value) {
if (!empty($value['name']) && !is_null($value['name'])) {
return $value;
}
});
<?php
$data = array();
$fixedData = array();
$countyName = array();
$numrow = 2;
echo "<pre>";
// insert to tb participant => 1
foreach($sheet as $key => $row){
if($this->split_name($row['B'])!=='' && $this->split_name($row['B'])!==NULL){
$data[] = array(
'name' => $this->split_name($row['B']),
'phone' => $row['D'],
'mobile' => $row['E'],
'institution' => $row['F'],
'departement' => $row['G'],
'address' => $row['H'],
'country' => $row['I'],
);
$numrow++;
}
}
//loop search data
var_dump ($data);
die();
I simple put an if condition inside your loop so you can check if your value is null or empty and if it is then you don't fill your new array. Also moved your counter inside the if so you increment it only in a success array push
A more "elegant" way for your if condition is this as well:
if (!empty($this->split_name($row['B'])) && !is_null($this->split_name($row['B'])))

php get object data from array of field names

I have a result set from a query that gives me an object that has dozens of fields, the below example is a subset:
[79] => stdClass Object
(
[name] => John Doe
[email] => john#doe.com
[ext] => 4004
[options] => stdClass Object
(
[type] => friend
[rating] => Excellent
[context] => default
)
[address] => 123 Anywhere St
)
Instead of plowing through each field, because I only want a handful of them, I am trying to use an array to get what i want:
$fields = array('name','email','options->type','options->rating','address');
so then i do:
foreach ($result as $k => $v){
foreach ($fields as $kk => $vv){
echo $kk. " - " . $v->$kk."<br>";
}
}
Which gives me field name and its value.
name - John Doe
email - john#doe.com
address - 123 Anywhere St.
However, anything in that sub object(options) is giving me a blank result.
You can use a recursive function providing that you don't mind changing the format of your $fields var to include arrays. In my opinion this makes it easier to read anyway, and easier to handle in code.
The benefit of using a recursive function is that it will handle any depth.
$o = (object) [
'name' => 'John Doe',
'email' => 'john#doe.com',
'ext' => 4004,
'options' => (object) [
'type' => 'friend',
'rating' => 'Excellent',
'context' => 'default',
'any' => (object) [
'depth' => (object) [
'will' => 'work'
]
]
],
'address' => '123 Anywhere St'
];
$fields = [
'name',
'email',
'options' => [
'type',
'rating',
'any' => [
'depth' => [
'will'
]
]
],
'address'
];
function getObjectProps($o, $fields, $parent = '') {
if (strlen($parent)) {
$parent .= '->';
}
foreach ($fields as $k => $v) {
if (is_array($v)) {
getObjectProps($o->{$k}, $v, $parent . $k);
} else {
echo $parent . $v . ' - ' . $o->{$v} . '<br/>';
}
}
}
getObjectProps($o, $fields);
Output:
name - John Doe
email - john#doe.com
options->type - friend
options->rating - Excellent
options->any->depth->will - work
address - 123 Anywhere St
Use a multidimensionnal array for $fields and change your foreach loop a little bit:
$fields = [
'name',
'email',
'options' => [
'type',
'rating'
],
'address'
];
foreach ($result as $obj) {
foreach ($fields as $k => $v) {
if (is_array($v)) {
foreach ($v as $vv) {
echo $vv . ' - ' . $obj->$k->$vv . '<br>';
}
} else {
echo $v . ' - ' . $obj->$v . '<br>';
}
}
}
Here's what I came up with in the meantime
$someObject = new stdClass();
$someObject->name = 'Dale';
$someObject->options = new stdClass();
$someObject->options->address = 'The Address';
$fields = ['name', 'options' => ['address']];
function toArray($object, $fields) {
$return = [];
foreach($fields as $fieldKey => $fieldValue) {
if(!is_array($fieldValue)) {
$return [] = $object->$fieldValue;
}
else
{
$return = array_merge($return, toArray($object->$fieldKey, $fieldValue));
}
}
return $return;
}
print_r(toArray($someObject, $fields));
Try this code:
$newResult = array_intersect_key((array)$result, array_flip($fields));
There is no easy way to access property like the way you are trying. But there's a symfony's property access component which you can use. But if you are focusing to use your own method then you might love the following code. I ran this code in PHP 7.2
$data = (object) [
'name' => 'John Doe',
'email' => 'john#doe.com',
'ext' => 4004,
'options' => (object) [
'type' => "friend",
'rating' => 'Excellent',
'context' => 'default'
],
'address' => '123 Anywhere St'
];
$fields = array('name','email','options->type','options->rating','address');
/*** way of initializing
$response = []; // get array value here
$response = ""; // get string value here
***/
$response = ""; //string value here... as output
/*** Don't you think that the below code now is clean and reusable? ***/
array_map(function($key) use($data, &$response) {
$value = array_reduce(explode('->', $key), function ($o, $p) {
return $o->$p;
}, $data);
is_array($response) ? ($response[$key] = $value) : ($response .= $key . ' - ' . $value . '<br>');
}, $fields);
echo $response;
In the above code if you initialize $response as an empty array then you'll get array as output. And if you initialize that as an empty string then you'll get string output. And I think instead of using for loop, use array_map and array_reduce, they work like magic and reduce your code too.
String as output:
Array as ouput:

Search values on multidimensional array then display the result

I'm trying to retrieve the values on multidimensional arrays using a search like function.
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
array(
'Name'=>'Dune',
'Year'=>'1984',
'Rating'=>'6'
),
array(
'Name'=>'Superbabies: Baby Geniuses 2',
'Year'=>'2004',
'Rating'=>'1'
)
);
So for example, if you want to search the array with a value of Name with 'City of God' and Year with '1963' the expected output should be like this
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
);
Currently, the function I am using is this
function multiSearch(array $array, array $pairs)
{
$found = array();
foreach ($array as $aKey => $aVal) {
$coincidences = 0;
foreach ($pairs as $pKey => $pVal) {
if (array_key_exists($pKey, $aVal) && $aVal[$pKey] == $pVal) {
$coincidences++;
}
}
if ($coincidences == count($pairs)) {
$found[$aKey] = $aVal;
}
}
return $found;
}
However, using this function only capable of retrieving data of the same array key. So for example if I search the value of Name of 'City of God'
$x = multiSearch($rows, array('Name' => 'City of God')
This will display the correct output like this
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
);
Unfortunately, if you try to use, 'Name' => 'City of God' and 'Year' => '1963'
It will not display any output. I'm stuck on figuring out on displaying the correct output, any ideas would be appreciated
Try this :
$datas = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
array(
'Name'=>'Dune',
'Year'=>'1984',
'Rating'=>'6'
),
array(
'Name'=>'Superbabies: Baby Geniuses 2',
'Year'=>'2004',
'Rating'=>'1'
)
);
$search = array(
'Name' => 'Dune',
'Year' => '2004'
);
$output = array();
foreach ($datas as $key1 => $row) {
foreach ($row as $key2 => $value) {
if($search[$key2] == $value) {
// if(stristr($value, $search[$key2]) !== FALSE) { // if you want to search
$output[] = $datas[$key1];
break;
}
}
}
echo "<pre>"; print_r($output); exit;
Output:
Array
(
[0] => Array
(
[Name] => Dune
[Year] => 1984
[Rating] => 6
)
[1] => Array
(
[Name] => Superbabies: Baby Geniuses 2
[Year] => 2004
[Rating] => 1
)
)
If you need OR, as you mentioned in your question, you then need to see if at least one coincidence happened:
if ($coincidences > 0) {
$found[$aKey] = $aVal;
}
This way, both entries:
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
);
will be matched.
Modify the search function to this - works for both the cases mentioned:
function multiSearch($rows, $value) {
$newArr = array();
foreach ($rows as $row) {
foreach ($value as $key => $val) {
if (isset($row[$key]) && $row[$key] == $val) {
$newArr[] = $row;
}
}
}
return $newArr;
}
Calling with one or two values produces the required output -
$x = multiSearch($rows, array('Name' => 'City of God'));
$x = multiSearch($rows, array('Name' => 'City of God', 'Year' => '1963'));
//returns $rows containing this pair
Take a look on my suggestion
function search ($storage, $criteria)
{
return array_filter(
$storage,
function ($row) use ($criteria) {
foreach ($criteria as $key => $value) {
if ($row[$key] === $value) {
return true;
}
}
}
);
}
$result = search($datas, $search);
/**
* $result = [
* 2 => [
* 'Name' => 'Dune'
* 'Year' => '1984'
* 'Rating' => '6'
* ],
* 3 => [
* 'Name' => 'Superbabies: Baby Geniuses 2'
* 'Year' => '2004'
* 'Rating' => '1'
* ]
* ];
*/
what about a recursive call?
function recursiveInArray ($needle, $array) {
$status = false;
foreach($array as $value) {
if(is_array($value)) {
recursiveInArray($needle, $value);
} else {
$status = ($needle == $value);
}
}
return $status;
}
i know its abstract, but should help you working with multi dimensional searching

Multi level menu from array in PHP

I'm trying to make a menu from an array in PHP.
I would like to make it more readable, so I used an array.
My array is the following:
$menu = array(
'calendar' => array(
'text' => 'Calendar',
'rights' => 'user'
),
'customers' => array(
'text' => 'Customers',
'rights' => 'user',
'sub' => array(
'create-new' => array(
'text' => 'Create new customer',
'rights' => 'user'
),
'show-customers' => array(
'text' => 'Show all customers',
'rights' => 'user'
)
)
)
);
And the PHP to parse the array:
buildMenu($menu);
function buildMenu($menu_array, $is_sub=FALSE) {
$attr = (!$is_sub) ? ' id="menu"' : ' class="submenu"';
$menu = "<ul".$attr.">";
foreach($menu_array as $id => $properties) {
foreach($properties as $key => $val) {
if(is_array($val)) {
$sub = buildMenu($val, TRUE);
}
else {
$sub = NULL;
$$key = $val;
}
}
if(!isset($url)) {
$url = $id;
}
$menu .= "<li>".$text."".$sub."</li>";
unset($url, $text, $sub);
}
return $menu . "</ul>";
}
Do I miss something ?
It don't echo me anything.
Thanks for your help.
Just return will not give output you need to either use below code, inside function.
echo $menu . "</ul>";
instead of
return $menu . "</ul>";
or use below code if you return your html output.
echo $output = buildMenu($menu);
instead of
buildMenu($menu);

Flatten multidimensional array concatenating keys [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
PHP convert nested array to single array while concatenating keys?
Get array's key recursively and create underscore seperated string
Please, read the whole question before answering.
I have this multidimensional array:
$data = array(
'user' => array(
'email' => 'user#example.com',
'name' => 'Super User',
'address' => array(
'billing' => 'Street 1',
'delivery' => 'Street 2'
)
),
'post' => 'Hello, World!'
);
I want it flatten, transformed into:
$data = array(
'user.email' => 'user#example.com',
'user.name' => 'Super User',
'user.address.billing' => 'Street 1',
'user.address.delivery' => 'Street 2',
'post' => 'Hello, World!'
);
Important:
The keys are very important to me. I want them concatenated, separated by periods.
It should work with any level of nesting.
Thank you!
Something like this should work:
function flatten($array, $prefix = '') {
$result = array();
foreach($array as $key=>$value) {
if(is_array($value)) {
$result = $result + flatten($value, $prefix . $key . '.');
}
else {
$result[$prefix . $key] = $value;
}
}
return $result;
}
DEMO
Thanks for all the given answers.
I have transformed it in the following, which is an improved version. It eliminates the need of a root prefix, does not need to use references, it is cleaner to read, and it has a better name:
function array_flat($array, $prefix = '')
{
$result = array();
foreach ($array as $key => $value)
{
$new_key = $prefix . (empty($prefix) ? '' : '.') . $key;
if (is_array($value))
{
$result = array_merge($result, array_flat($value, $new_key));
}
else
{
$result[$new_key] = $value;
}
}
return $result;
}
Try this
<?php
$data = array(
'user' => array(
'email' => 'user#example.com',
'name' => 'Super User',
'address' => array(
'billing' => 'Street 1',
'delivery' => 'Street 2'
)
),
'post' => 'Hello, World!'
);
function prefixKey($prefix, $array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
$result = array_merge($result, prefixKey($prefix . $key . '.', $value));
else
$result[$prefix . $key] = $value;
}
return $result;
}
var_dump(prefixKey('', $data));
?>
Outputs
array
'user.email' => string 'user#example.com' (length=16)
'user.name' => string 'Super User' (length=10)
'user.address.billing' => string 'Street 1' (length=8)
'user.address.delivery' => string 'Street 2' (length=8)
'post' => string 'Hello, World!' (length=13)
test this out here
i passed by reference so no need for returns. just hand over the array storage.
$store = array();
function flatten($array,&$storage,$parentKey = ''){
foreach($array as $key => $value){
$itemKey = (($parentKey)? $parentKey.'.':'').$key;
if(is_array($value)){
flatten($value,$storage,$itemKey);
} else {
$storage[$itemKey] = $value;
}
}
}
flatten($data,$store);
var_dump($store);
Use recursion such as this:
function process_data( $data, $parent_key ){
if ( ! is_array( $data ) ){
return $data;
}
$flattened_array = array();
foreach( $data as $key => $item ){
$flattened_key = $parent_key . '.' . $key;
$flattened_array[ $flattened_key ] = process_data( $item, $flattened_key );
}
return $flattened_array;
}

Categories