Related
Just as the title implies, I am trying to create a parser and trying to find the optimal solution to convert something from dot namespace into a multidimensional array such that
s1.t1.column.1 = size:33%
would be the same as
$source['s1']['t1']['column']['1'] = 'size:33%';
Try this number...
function assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
CodePad
It will loop through the keys (delimited with . by default) to get to the final property, and then do assignment on the value.
If some of the keys aren't present, they're created.
FYI In Laravel we have a array_set() helper function which translates in this function
Method to store in an array using dot notation
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* #param array $array
* #param string $key
* #param mixed $value
* #return array
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) {
return $array = $value;
}
$keys = explode('.', $key);
while (count($keys) > 1) {
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if (! isset($array[$key]) || ! is_array($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
It's simple as
$array = ['products' => ['desk' => ['price' => 100]]];
array_set($array, 'products.desk.price', 200);
// ['products' => ['desk' => ['price' => 200]]]
You may check it in the docs
If you need to instead get the data using dot notation the process is a bit longer, but served on a plate by array_get() which translates to this function (actually the linked source shows you all the helper array related class)
Method to read from an an array using dot notation
/**
* Get an item from an array using "dot" notation.
*
* #param \ArrayAccess|array $array
* #param string $key
* #param mixed $default
* #return mixed
*/
public static function get($array, $key, $default = null)
{
if (! static::accessible($array)) {
return value($default);
}
if (is_null($key)) {
return $array;
}
if (static::exists($array, $key)) {
return $array[$key];
}
if (strpos($key, '.') === false) {
return $array[$key] ?? value($default);
}
foreach (explode('.', $key) as $segment) {
if (static::accessible($array) && static::exists($array, $segment)) {
$array = $array[$segment];
} else {
return value($default);
}
}
return $array;
}
As you can see, it uses two submethods, accessible() and exists()
/**
* Determine whether the given value is array accessible.
*
* #param mixed $value
* #return bool
*/
public static function accessible($value)
{
return is_array($value) || $value instanceof ArrayAccess;
}
And
/**
* Determine if the given key exists in the provided array.
*
* #param \ArrayAccess|array $array
* #param string|int $key
* #return bool
*/
public static function exists($array, $key)
{
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
Last thing it uses, but you can probably skip that, is value() which is
if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* #param mixed $value
* #return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
You can use this function to convert dot notation arrray to multidimensional array.
function flattenToMultiDimensional(array $array, $delimiter = '.')
{
$result = [];
foreach ($array as $notations => $value) {
// extract keys
$keys = explode($delimiter, $notations);
// reverse keys for assignments
$keys = array_reverse($keys);
// set initial value
$lastVal = $value;
foreach ($keys as $key) {
// wrap value with key over each iteration
$lastVal = [
$key => $lastVal
];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
Example:
$array = [
'test.example.key' => 'value'
];
print_r(flattenToMultiDimensional($array));
Output:
Array
(
[test] => Array
(
[example] => Array
(
[key] => value
)
)
)
I would suggest using dflydev/dot-access-data.
If you're not familiar with using Composer, head over to https://getcomposer.org/ for an introduction so that you can download and autoload the package as as dependency for your project.
Once you have the package, you can load a multi-dimensional array into a Data object:
use Dflydev\DotAccessData\Data;
$data = new Data(array(
's1' => array(
't1' => array(
'column' => array(
'1' => 'size:33%',
),
),
),
);
And access the values using dot notation:
$size = $username = $data->get('s1.t1.column.1');
You should try this one : https://symfony.com/doc/current/components/property_access.html
It allows you to access anything like arrays, objects, setters, getters, etc... with doted notation.
You can use it standalone without symfony. If your project use composer, it's a one line install.
Here is my solution:
<?php
// worked with any lvl
function unFlatArr($array, $delimiter = '_')
{
$result = [];
foreach ($array as $notations => $value) {
$keys = explode($delimiter, $notations);
$keys = array_reverse($keys);
$lastVal = $value;
foreach ($keys as $key) {
$lastVal = [$key => $lastVal];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
$array = ['s1.t1.column.1' => 'size:33%'];
var_export(unFlatArr($array, '.'));
Although pasrse_ini_file() can also bring out multidimensional array, I will present a different solution. Zend_Config_Ini()
$conf = new Zend_COnfig_Ini("path/to/file.ini");
echo $conf -> one -> two -> three; // This is how easy it is to do so
//prints one.two.three
I found a solution that worked for me at: Convert Flat PHP Array to Nested Array based on Array Keys and since I had an array based on an .ini file with different keys I made a tiny modification of that script and made work for me.
My array looked like this:
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => qwer
[resources.db.params.username] => asdf
...
On request, this is the code that I described was working for me:
<?php
echo "remove the exit :-)"; exit;
$db_settings = parse_ini_file($_SERVER['DOCUMENT_ROOT'].'/website/var/config/app.ini');
echo "<pre>";
print_r($db_settings);
echo "</pre>";
$resources = array();
foreach ($db_settings as $path => $value) {
$ancestors = explode('.', $path);
set_nested_value($resources, $ancestors, $value);
}
echo "<pre>";
print_r($resources);
echo "</pre>";
/**
* Give it and array, and an array of parents, it will decent into the
* nested arrays and set the value.
*/
function set_nested_value(array &$arr, array $ancestors, $value) {
$current = &$arr;
foreach ($ancestors as $key) {
// To handle the original input, if an item is not an array,
// replace it with an array with the value as the first item.
if (!is_array($current)) {
$current = array( $current);
}
if (!array_key_exists($key, $current)) {
$current[$key] = array();
}
$current = &$current[$key];
}
$current = $value;
}
This is the source of the .ini file read by the parse_ini_file():
Array
(
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => dbname
[resources.db.params.username] => dbname_user
[resources.db.params.password] => qwerqwerqwerqwer
[resources.db.params.charset] => utf8
[externaldb.adapter] => PDO_MYSQL
[externaldb.params.host] => localhost
[externaldb.params.dbname] => dbname2
[externaldb.params.username] => dbname_user2
[externaldb.params.password] => qwerqwerwqerqerw
[externaldb.params.charset] => latin1
)
This is the outcome of the code above:
Array
(
[resources] => Array
(
[db] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname
[username] => dbname_user
[password] => qwerqwerqwerqwer
[charset] => utf8
)
)
)
[externaldb] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname2
[username] => dbname_user2
[password] => qwerqwerwqerqerw
[charset] => latin1
)
)
)
I am pretty sure you are trying to do this to store some configuration data or similar.
I highly suggest you to save such file as .ini and use parse_ini_file() function to change the configuration data into a multidimensional array. As simple as this
$confArray = parse_ini_file("filename.ini");
var_dump($confArray);
Quick and dirty...
<?php
$input = 'one.two.three = four';
list($key, $value) = explode('=', $input);
foreach (explode('.', $key) as $keyName) {
if (false === isset($source)) {
$source = array();
$sourceRef = &$source;
}
$keyName = trim($keyName);
$sourceRef = &$sourceRef[$keyName];
}
$sourceRef = $value;
unset($sourceRef);
var_dump($source);
Just as the title implies, I am trying to create a parser and trying to find the optimal solution to convert something from dot namespace into a multidimensional array such that
s1.t1.column.1 = size:33%
would be the same as
$source['s1']['t1']['column']['1'] = 'size:33%';
Try this number...
function assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
CodePad
It will loop through the keys (delimited with . by default) to get to the final property, and then do assignment on the value.
If some of the keys aren't present, they're created.
FYI In Laravel we have a array_set() helper function which translates in this function
Method to store in an array using dot notation
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* #param array $array
* #param string $key
* #param mixed $value
* #return array
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) {
return $array = $value;
}
$keys = explode('.', $key);
while (count($keys) > 1) {
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if (! isset($array[$key]) || ! is_array($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
It's simple as
$array = ['products' => ['desk' => ['price' => 100]]];
array_set($array, 'products.desk.price', 200);
// ['products' => ['desk' => ['price' => 200]]]
You may check it in the docs
If you need to instead get the data using dot notation the process is a bit longer, but served on a plate by array_get() which translates to this function (actually the linked source shows you all the helper array related class)
Method to read from an an array using dot notation
/**
* Get an item from an array using "dot" notation.
*
* #param \ArrayAccess|array $array
* #param string $key
* #param mixed $default
* #return mixed
*/
public static function get($array, $key, $default = null)
{
if (! static::accessible($array)) {
return value($default);
}
if (is_null($key)) {
return $array;
}
if (static::exists($array, $key)) {
return $array[$key];
}
if (strpos($key, '.') === false) {
return $array[$key] ?? value($default);
}
foreach (explode('.', $key) as $segment) {
if (static::accessible($array) && static::exists($array, $segment)) {
$array = $array[$segment];
} else {
return value($default);
}
}
return $array;
}
As you can see, it uses two submethods, accessible() and exists()
/**
* Determine whether the given value is array accessible.
*
* #param mixed $value
* #return bool
*/
public static function accessible($value)
{
return is_array($value) || $value instanceof ArrayAccess;
}
And
/**
* Determine if the given key exists in the provided array.
*
* #param \ArrayAccess|array $array
* #param string|int $key
* #return bool
*/
public static function exists($array, $key)
{
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
Last thing it uses, but you can probably skip that, is value() which is
if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* #param mixed $value
* #return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
You can use this function to convert dot notation arrray to multidimensional array.
function flattenToMultiDimensional(array $array, $delimiter = '.')
{
$result = [];
foreach ($array as $notations => $value) {
// extract keys
$keys = explode($delimiter, $notations);
// reverse keys for assignments
$keys = array_reverse($keys);
// set initial value
$lastVal = $value;
foreach ($keys as $key) {
// wrap value with key over each iteration
$lastVal = [
$key => $lastVal
];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
Example:
$array = [
'test.example.key' => 'value'
];
print_r(flattenToMultiDimensional($array));
Output:
Array
(
[test] => Array
(
[example] => Array
(
[key] => value
)
)
)
I would suggest using dflydev/dot-access-data.
If you're not familiar with using Composer, head over to https://getcomposer.org/ for an introduction so that you can download and autoload the package as as dependency for your project.
Once you have the package, you can load a multi-dimensional array into a Data object:
use Dflydev\DotAccessData\Data;
$data = new Data(array(
's1' => array(
't1' => array(
'column' => array(
'1' => 'size:33%',
),
),
),
);
And access the values using dot notation:
$size = $username = $data->get('s1.t1.column.1');
You should try this one : https://symfony.com/doc/current/components/property_access.html
It allows you to access anything like arrays, objects, setters, getters, etc... with doted notation.
You can use it standalone without symfony. If your project use composer, it's a one line install.
Here is my solution:
<?php
// worked with any lvl
function unFlatArr($array, $delimiter = '_')
{
$result = [];
foreach ($array as $notations => $value) {
$keys = explode($delimiter, $notations);
$keys = array_reverse($keys);
$lastVal = $value;
foreach ($keys as $key) {
$lastVal = [$key => $lastVal];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
$array = ['s1.t1.column.1' => 'size:33%'];
var_export(unFlatArr($array, '.'));
Although pasrse_ini_file() can also bring out multidimensional array, I will present a different solution. Zend_Config_Ini()
$conf = new Zend_COnfig_Ini("path/to/file.ini");
echo $conf -> one -> two -> three; // This is how easy it is to do so
//prints one.two.three
I found a solution that worked for me at: Convert Flat PHP Array to Nested Array based on Array Keys and since I had an array based on an .ini file with different keys I made a tiny modification of that script and made work for me.
My array looked like this:
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => qwer
[resources.db.params.username] => asdf
...
On request, this is the code that I described was working for me:
<?php
echo "remove the exit :-)"; exit;
$db_settings = parse_ini_file($_SERVER['DOCUMENT_ROOT'].'/website/var/config/app.ini');
echo "<pre>";
print_r($db_settings);
echo "</pre>";
$resources = array();
foreach ($db_settings as $path => $value) {
$ancestors = explode('.', $path);
set_nested_value($resources, $ancestors, $value);
}
echo "<pre>";
print_r($resources);
echo "</pre>";
/**
* Give it and array, and an array of parents, it will decent into the
* nested arrays and set the value.
*/
function set_nested_value(array &$arr, array $ancestors, $value) {
$current = &$arr;
foreach ($ancestors as $key) {
// To handle the original input, if an item is not an array,
// replace it with an array with the value as the first item.
if (!is_array($current)) {
$current = array( $current);
}
if (!array_key_exists($key, $current)) {
$current[$key] = array();
}
$current = &$current[$key];
}
$current = $value;
}
This is the source of the .ini file read by the parse_ini_file():
Array
(
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => dbname
[resources.db.params.username] => dbname_user
[resources.db.params.password] => qwerqwerqwerqwer
[resources.db.params.charset] => utf8
[externaldb.adapter] => PDO_MYSQL
[externaldb.params.host] => localhost
[externaldb.params.dbname] => dbname2
[externaldb.params.username] => dbname_user2
[externaldb.params.password] => qwerqwerwqerqerw
[externaldb.params.charset] => latin1
)
This is the outcome of the code above:
Array
(
[resources] => Array
(
[db] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname
[username] => dbname_user
[password] => qwerqwerqwerqwer
[charset] => utf8
)
)
)
[externaldb] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname2
[username] => dbname_user2
[password] => qwerqwerwqerqerw
[charset] => latin1
)
)
)
I am pretty sure you are trying to do this to store some configuration data or similar.
I highly suggest you to save such file as .ini and use parse_ini_file() function to change the configuration data into a multidimensional array. As simple as this
$confArray = parse_ini_file("filename.ini");
var_dump($confArray);
Quick and dirty...
<?php
$input = 'one.two.three = four';
list($key, $value) = explode('=', $input);
foreach (explode('.', $key) as $keyName) {
if (false === isset($source)) {
$source = array();
$sourceRef = &$source;
}
$keyName = trim($keyName);
$sourceRef = &$sourceRef[$keyName];
}
$sourceRef = $value;
unset($sourceRef);
var_dump($source);
Just as the title implies, I am trying to create a parser and trying to find the optimal solution to convert something from dot namespace into a multidimensional array such that
s1.t1.column.1 = size:33%
would be the same as
$source['s1']['t1']['column']['1'] = 'size:33%';
Try this number...
function assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
CodePad
It will loop through the keys (delimited with . by default) to get to the final property, and then do assignment on the value.
If some of the keys aren't present, they're created.
FYI In Laravel we have a array_set() helper function which translates in this function
Method to store in an array using dot notation
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* #param array $array
* #param string $key
* #param mixed $value
* #return array
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) {
return $array = $value;
}
$keys = explode('.', $key);
while (count($keys) > 1) {
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if (! isset($array[$key]) || ! is_array($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
It's simple as
$array = ['products' => ['desk' => ['price' => 100]]];
array_set($array, 'products.desk.price', 200);
// ['products' => ['desk' => ['price' => 200]]]
You may check it in the docs
If you need to instead get the data using dot notation the process is a bit longer, but served on a plate by array_get() which translates to this function (actually the linked source shows you all the helper array related class)
Method to read from an an array using dot notation
/**
* Get an item from an array using "dot" notation.
*
* #param \ArrayAccess|array $array
* #param string $key
* #param mixed $default
* #return mixed
*/
public static function get($array, $key, $default = null)
{
if (! static::accessible($array)) {
return value($default);
}
if (is_null($key)) {
return $array;
}
if (static::exists($array, $key)) {
return $array[$key];
}
if (strpos($key, '.') === false) {
return $array[$key] ?? value($default);
}
foreach (explode('.', $key) as $segment) {
if (static::accessible($array) && static::exists($array, $segment)) {
$array = $array[$segment];
} else {
return value($default);
}
}
return $array;
}
As you can see, it uses two submethods, accessible() and exists()
/**
* Determine whether the given value is array accessible.
*
* #param mixed $value
* #return bool
*/
public static function accessible($value)
{
return is_array($value) || $value instanceof ArrayAccess;
}
And
/**
* Determine if the given key exists in the provided array.
*
* #param \ArrayAccess|array $array
* #param string|int $key
* #return bool
*/
public static function exists($array, $key)
{
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
Last thing it uses, but you can probably skip that, is value() which is
if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* #param mixed $value
* #return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
You can use this function to convert dot notation arrray to multidimensional array.
function flattenToMultiDimensional(array $array, $delimiter = '.')
{
$result = [];
foreach ($array as $notations => $value) {
// extract keys
$keys = explode($delimiter, $notations);
// reverse keys for assignments
$keys = array_reverse($keys);
// set initial value
$lastVal = $value;
foreach ($keys as $key) {
// wrap value with key over each iteration
$lastVal = [
$key => $lastVal
];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
Example:
$array = [
'test.example.key' => 'value'
];
print_r(flattenToMultiDimensional($array));
Output:
Array
(
[test] => Array
(
[example] => Array
(
[key] => value
)
)
)
I would suggest using dflydev/dot-access-data.
If you're not familiar with using Composer, head over to https://getcomposer.org/ for an introduction so that you can download and autoload the package as as dependency for your project.
Once you have the package, you can load a multi-dimensional array into a Data object:
use Dflydev\DotAccessData\Data;
$data = new Data(array(
's1' => array(
't1' => array(
'column' => array(
'1' => 'size:33%',
),
),
),
);
And access the values using dot notation:
$size = $username = $data->get('s1.t1.column.1');
You should try this one : https://symfony.com/doc/current/components/property_access.html
It allows you to access anything like arrays, objects, setters, getters, etc... with doted notation.
You can use it standalone without symfony. If your project use composer, it's a one line install.
Here is my solution:
<?php
// worked with any lvl
function unFlatArr($array, $delimiter = '_')
{
$result = [];
foreach ($array as $notations => $value) {
$keys = explode($delimiter, $notations);
$keys = array_reverse($keys);
$lastVal = $value;
foreach ($keys as $key) {
$lastVal = [$key => $lastVal];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
$array = ['s1.t1.column.1' => 'size:33%'];
var_export(unFlatArr($array, '.'));
Although pasrse_ini_file() can also bring out multidimensional array, I will present a different solution. Zend_Config_Ini()
$conf = new Zend_COnfig_Ini("path/to/file.ini");
echo $conf -> one -> two -> three; // This is how easy it is to do so
//prints one.two.three
I found a solution that worked for me at: Convert Flat PHP Array to Nested Array based on Array Keys and since I had an array based on an .ini file with different keys I made a tiny modification of that script and made work for me.
My array looked like this:
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => qwer
[resources.db.params.username] => asdf
...
On request, this is the code that I described was working for me:
<?php
echo "remove the exit :-)"; exit;
$db_settings = parse_ini_file($_SERVER['DOCUMENT_ROOT'].'/website/var/config/app.ini');
echo "<pre>";
print_r($db_settings);
echo "</pre>";
$resources = array();
foreach ($db_settings as $path => $value) {
$ancestors = explode('.', $path);
set_nested_value($resources, $ancestors, $value);
}
echo "<pre>";
print_r($resources);
echo "</pre>";
/**
* Give it and array, and an array of parents, it will decent into the
* nested arrays and set the value.
*/
function set_nested_value(array &$arr, array $ancestors, $value) {
$current = &$arr;
foreach ($ancestors as $key) {
// To handle the original input, if an item is not an array,
// replace it with an array with the value as the first item.
if (!is_array($current)) {
$current = array( $current);
}
if (!array_key_exists($key, $current)) {
$current[$key] = array();
}
$current = &$current[$key];
}
$current = $value;
}
This is the source of the .ini file read by the parse_ini_file():
Array
(
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => dbname
[resources.db.params.username] => dbname_user
[resources.db.params.password] => qwerqwerqwerqwer
[resources.db.params.charset] => utf8
[externaldb.adapter] => PDO_MYSQL
[externaldb.params.host] => localhost
[externaldb.params.dbname] => dbname2
[externaldb.params.username] => dbname_user2
[externaldb.params.password] => qwerqwerwqerqerw
[externaldb.params.charset] => latin1
)
This is the outcome of the code above:
Array
(
[resources] => Array
(
[db] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname
[username] => dbname_user
[password] => qwerqwerqwerqwer
[charset] => utf8
)
)
)
[externaldb] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname2
[username] => dbname_user2
[password] => qwerqwerwqerqerw
[charset] => latin1
)
)
)
I am pretty sure you are trying to do this to store some configuration data or similar.
I highly suggest you to save such file as .ini and use parse_ini_file() function to change the configuration data into a multidimensional array. As simple as this
$confArray = parse_ini_file("filename.ini");
var_dump($confArray);
Quick and dirty...
<?php
$input = 'one.two.three = four';
list($key, $value) = explode('=', $input);
foreach (explode('.', $key) as $keyName) {
if (false === isset($source)) {
$source = array();
$sourceRef = &$source;
}
$keyName = trim($keyName);
$sourceRef = &$sourceRef[$keyName];
}
$sourceRef = $value;
unset($sourceRef);
var_dump($source);
Just as the title implies, I am trying to create a parser and trying to find the optimal solution to convert something from dot namespace into a multidimensional array such that
s1.t1.column.1 = size:33%
would be the same as
$source['s1']['t1']['column']['1'] = 'size:33%';
Try this number...
function assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
CodePad
It will loop through the keys (delimited with . by default) to get to the final property, and then do assignment on the value.
If some of the keys aren't present, they're created.
FYI In Laravel we have a array_set() helper function which translates in this function
Method to store in an array using dot notation
/**
* Set an array item to a given value using "dot" notation.
*
* If no key is given to the method, the entire array will be replaced.
*
* #param array $array
* #param string $key
* #param mixed $value
* #return array
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) {
return $array = $value;
}
$keys = explode('.', $key);
while (count($keys) > 1) {
$key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an empty array
// to hold the next value, allowing us to create the arrays to hold final
// values at the correct depth. Then we'll keep digging into the array.
if (! isset($array[$key]) || ! is_array($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$array[array_shift($keys)] = $value;
return $array;
}
It's simple as
$array = ['products' => ['desk' => ['price' => 100]]];
array_set($array, 'products.desk.price', 200);
// ['products' => ['desk' => ['price' => 200]]]
You may check it in the docs
If you need to instead get the data using dot notation the process is a bit longer, but served on a plate by array_get() which translates to this function (actually the linked source shows you all the helper array related class)
Method to read from an an array using dot notation
/**
* Get an item from an array using "dot" notation.
*
* #param \ArrayAccess|array $array
* #param string $key
* #param mixed $default
* #return mixed
*/
public static function get($array, $key, $default = null)
{
if (! static::accessible($array)) {
return value($default);
}
if (is_null($key)) {
return $array;
}
if (static::exists($array, $key)) {
return $array[$key];
}
if (strpos($key, '.') === false) {
return $array[$key] ?? value($default);
}
foreach (explode('.', $key) as $segment) {
if (static::accessible($array) && static::exists($array, $segment)) {
$array = $array[$segment];
} else {
return value($default);
}
}
return $array;
}
As you can see, it uses two submethods, accessible() and exists()
/**
* Determine whether the given value is array accessible.
*
* #param mixed $value
* #return bool
*/
public static function accessible($value)
{
return is_array($value) || $value instanceof ArrayAccess;
}
And
/**
* Determine if the given key exists in the provided array.
*
* #param \ArrayAccess|array $array
* #param string|int $key
* #return bool
*/
public static function exists($array, $key)
{
if ($array instanceof ArrayAccess) {
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
Last thing it uses, but you can probably skip that, is value() which is
if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* #param mixed $value
* #return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
You can use this function to convert dot notation arrray to multidimensional array.
function flattenToMultiDimensional(array $array, $delimiter = '.')
{
$result = [];
foreach ($array as $notations => $value) {
// extract keys
$keys = explode($delimiter, $notations);
// reverse keys for assignments
$keys = array_reverse($keys);
// set initial value
$lastVal = $value;
foreach ($keys as $key) {
// wrap value with key over each iteration
$lastVal = [
$key => $lastVal
];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
Example:
$array = [
'test.example.key' => 'value'
];
print_r(flattenToMultiDimensional($array));
Output:
Array
(
[test] => Array
(
[example] => Array
(
[key] => value
)
)
)
I would suggest using dflydev/dot-access-data.
If you're not familiar with using Composer, head over to https://getcomposer.org/ for an introduction so that you can download and autoload the package as as dependency for your project.
Once you have the package, you can load a multi-dimensional array into a Data object:
use Dflydev\DotAccessData\Data;
$data = new Data(array(
's1' => array(
't1' => array(
'column' => array(
'1' => 'size:33%',
),
),
),
);
And access the values using dot notation:
$size = $username = $data->get('s1.t1.column.1');
You should try this one : https://symfony.com/doc/current/components/property_access.html
It allows you to access anything like arrays, objects, setters, getters, etc... with doted notation.
You can use it standalone without symfony. If your project use composer, it's a one line install.
Here is my solution:
<?php
// worked with any lvl
function unFlatArr($array, $delimiter = '_')
{
$result = [];
foreach ($array as $notations => $value) {
$keys = explode($delimiter, $notations);
$keys = array_reverse($keys);
$lastVal = $value;
foreach ($keys as $key) {
$lastVal = [$key => $lastVal];
}
// merge result
$result = array_merge_recursive($result, $lastVal);
}
return $result;
}
$array = ['s1.t1.column.1' => 'size:33%'];
var_export(unFlatArr($array, '.'));
Although pasrse_ini_file() can also bring out multidimensional array, I will present a different solution. Zend_Config_Ini()
$conf = new Zend_COnfig_Ini("path/to/file.ini");
echo $conf -> one -> two -> three; // This is how easy it is to do so
//prints one.two.three
I found a solution that worked for me at: Convert Flat PHP Array to Nested Array based on Array Keys and since I had an array based on an .ini file with different keys I made a tiny modification of that script and made work for me.
My array looked like this:
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => qwer
[resources.db.params.username] => asdf
...
On request, this is the code that I described was working for me:
<?php
echo "remove the exit :-)"; exit;
$db_settings = parse_ini_file($_SERVER['DOCUMENT_ROOT'].'/website/var/config/app.ini');
echo "<pre>";
print_r($db_settings);
echo "</pre>";
$resources = array();
foreach ($db_settings as $path => $value) {
$ancestors = explode('.', $path);
set_nested_value($resources, $ancestors, $value);
}
echo "<pre>";
print_r($resources);
echo "</pre>";
/**
* Give it and array, and an array of parents, it will decent into the
* nested arrays and set the value.
*/
function set_nested_value(array &$arr, array $ancestors, $value) {
$current = &$arr;
foreach ($ancestors as $key) {
// To handle the original input, if an item is not an array,
// replace it with an array with the value as the first item.
if (!is_array($current)) {
$current = array( $current);
}
if (!array_key_exists($key, $current)) {
$current[$key] = array();
}
$current = &$current[$key];
}
$current = $value;
}
This is the source of the .ini file read by the parse_ini_file():
Array
(
[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => dbname
[resources.db.params.username] => dbname_user
[resources.db.params.password] => qwerqwerqwerqwer
[resources.db.params.charset] => utf8
[externaldb.adapter] => PDO_MYSQL
[externaldb.params.host] => localhost
[externaldb.params.dbname] => dbname2
[externaldb.params.username] => dbname_user2
[externaldb.params.password] => qwerqwerwqerqerw
[externaldb.params.charset] => latin1
)
This is the outcome of the code above:
Array
(
[resources] => Array
(
[db] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname
[username] => dbname_user
[password] => qwerqwerqwerqwer
[charset] => utf8
)
)
)
[externaldb] => Array
(
[adapter] => PDO_MYSQL
[params] => Array
(
[host] => localhost
[dbname] => dbname2
[username] => dbname_user2
[password] => qwerqwerwqerqerw
[charset] => latin1
)
)
)
I am pretty sure you are trying to do this to store some configuration data or similar.
I highly suggest you to save such file as .ini and use parse_ini_file() function to change the configuration data into a multidimensional array. As simple as this
$confArray = parse_ini_file("filename.ini");
var_dump($confArray);
Quick and dirty...
<?php
$input = 'one.two.three = four';
list($key, $value) = explode('=', $input);
foreach (explode('.', $key) as $keyName) {
if (false === isset($source)) {
$source = array();
$sourceRef = &$source;
}
$keyName = trim($keyName);
$sourceRef = &$sourceRef[$keyName];
}
$sourceRef = $value;
unset($sourceRef);
var_dump($source);
I have an array as follows
array(2) {
["operator"] => array(2) {
["qty"] => int(2)
["id"] => int(251)
}
["accessory209"] => array(2) {
["qty"] => int(1)
["id"] => int(209)
}
["accessory211"] => array(2) {
["qty"] => int(1)
["id"] => int(211)
}
}
I'm trying to find a way to verify an id value exists within the array and return bool. I'm trying to figure out a quick way that doesn't require creating a loop. Using the in_array function did not work, and I also read that it is quite slow.
In the php manual someone recommended using flip_array() and then isset(), but I can't get it to work for a 2-d array.
doing something like
if($array['accessory']['id'] == 211)
would also work for me, but I need to match all keys containing accessory -- not sure how to do that
Anyways, I'm spinning in circles, and could use some help.
This seems like it should be easy. Thanks.
array_walk() can be used to check whether a particular value is within the array; - it iterates through all the array elements which are passed to the function provided as second argument. For example, the function can be called as in the following code.
function checkValue($value, $key) {
echo $value['id'];
}
$arr = array(
'one' => array('id' => 1),
'two' => array('id' => 2),
'three' => array('id' => 3)
);
array_walk($arr, 'checkValue');
This function is useful in_array(211, $array['accessory']); It verifies the whole specified array to see if your value exists in there and returns true.
in_array
$map = array();
foreach ($arr as $v) {
$map[$v['id']] = 1;
}
//then you can just do this as when needed
$exists = isset($map[211]);
Or if you need the data associated with it
$map = array();
foreach ($arr as $k => $v) {
$map[$v['id']][$k] = $v;
}
print_r($map[211]);
<?php
//PHP 5.3 way to do it
function searchNestedArray(array $array, $search, $mode = 'value') {
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
if ($search === ${${"mode"}})
return true;
}
return false;
}
$data = array(
array('abc', 'ddd'),
'ccc',
'bbb',
array('aaa', array('yyy', 'mp' => 555))
);
var_dump(searchNestedArray($data, 555));
I used a static method because i needed it in a class, but if you want you can use it as a simple function.
/**
* Given an array like this
* array(
* 'id' => "first",
* 'title' => "First Toolbar",
* 'class' => "col-md-12",
* 'items' => array(
* array(
* 'tipo' => "clientLink",
* 'id' => "clientLinkInsert"
* )
* )
* )
*
* and array search like this
* array("items", 0, "id")
*
* Find the path described by the array search, in the original array
*
* #param array $array source array
* #param array $search the path to the item es. ["items", 0, "tipo"]
* #param null|mixed $defaultIfNotFound the default value if the value is not found
*
* #return mixed
*/
public static function getNestedKey($array, $search, $defaultIfNotFound = null)
{
if( count($search) == 0 ) return $defaultIfNotFound;
else{
$firstElementSearch = self::array_kshift($search);
if (array_key_exists($firstElementSearch, $array)) {
if( count($search) == 0 )
return $array[$firstElementSearch];
else
return self::getNestedKey($array[$firstElementSearch], $search, $defaultIfNotFound);
}else{
return $defaultIfNotFound;
}
}
}
You can use
Arr::getNestedElement($array, $keys, $default = null)
from this library to get value from multidimensional array using keys specified like 'key1.key2.key3' or ['key1', 'key2', 'key3'] and fallback to default value if no element was found. Using your example it will look like:
if (Arr::getNestedElement($array, 'accessory.id') == 211)