Retrieve value from multidimensional array using recursive keys [duplicate] - php

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);

Related

Convert a string of numbers to get a value from a multidimensional array [duplicate]

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);

Creating a dynamic parsable call to a multi-dimensional array key so it's not treated verbatim [duplicate]

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);

Trouble renaming array key in PHP [duplicate]

This question already has answers here:
PHP rename array keys in multidimensional array
(10 answers)
Closed last month.
When I var_dump on a variable called $tags (a multidimensional array) I get this:
Array
(
[0] => Array
(
[name] => tabbing
[url] => tabbing
)
[1] => Array
(
[name] => tabby ridiman
[url] => tabby-ridiman
)
[2] => Array
(
[name] => tables
[url] => tables
)
[3] => Array
(
[name] => tabloids
[url] => tabloids
)
[4] => Array
(
[name] => taco bell
[url] => taco-bell
)
[5] => Array
(
[name] => tacos
[url] => tacos
)
)
I would like to rename all array keys called "url" to be called "value". What would be a good way to do this?
You could use array_map() to do it.
$tags = array_map(function($tag) {
return array(
'name' => $tag['name'],
'value' => $tag['url']
);
}, $tags);
Loop through, set new key, unset old key.
foreach($tags as &$val){
$val['value'] = $val['url'];
unset($val['url']);
}
Talking about functional PHP, I have this more generic answer:
array_map(function($arr){
$ret = $arr;
$ret['value'] = $ret['url'];
unset($ret['url']);
return $ret;
}, $tag);
}
Recursive php rename keys function:
function replaceKeys($oldKey, $newKey, array $input){
$return = array();
foreach ($input as $key => $value) {
if ($key===$oldKey)
$key = $newKey;
if (is_array($value))
$value = replaceKeys( $oldKey, $newKey, $value);
$return[$key] = $value;
}
return $return;
}
foreach ($basearr as &$row)
{
$row['value'] = $row['url'];
unset( $row['url'] );
}
unset($row);
This should work in most versions of PHP 4+. Array map using anonymous functions is not supported below 5.3.
Also the foreach examples will throw a warning when using strict PHP error handling.
Here is a small multi-dimensional key renaming function. It can also be used to process arrays to have the correct keys for integrity throughout your app. It will not throw any errors when a key does not exist.
function multi_rename_key(&$array, $old_keys, $new_keys)
{
if(!is_array($array)){
($array=="") ? $array=array() : false;
return $array;
}
foreach($array as &$arr){
if (is_array($old_keys))
{
foreach($new_keys as $k => $new_key)
{
(isset($old_keys[$k])) ? true : $old_keys[$k]=NULL;
$arr[$new_key] = (isset($arr[$old_keys[$k]]) ? $arr[$old_keys[$k]] : null);
unset($arr[$old_keys[$k]]);
}
}else{
$arr[$new_keys] = (isset($arr[$old_keys]) ? $arr[$old_keys] : null);
unset($arr[$old_keys]);
}
}
return $array;
}
Usage is simple. You can either change a single key like in your example:
multi_rename_key($tags, "url", "value");
or a more complex multikey
multi_rename_key($tags, array("url","name"), array("value","title"));
It uses similar syntax as preg_replace() where the amount of $old_keys and $new_keys should be the same. However when they are not a blank key is added. This means you can use it to add a sort if schema to your array.
Use this all the time, hope it helps!
Very simple approach to replace keys in a multidimensional array, and maybe even a bit dangerous, but should work fine if you have some kind of control over the source array:
$array = [ 'oldkey' => [ 'oldkey' => 'wow'] ];
$new_array = json_decode(str_replace('"oldkey":', '"newkey":', json_encode($array)));
print_r($new_array); // [ 'newkey' => [ 'newkey' => 'wow'] ]
This doesn't have to be difficult in the least. You can simply assign the arrays around regardless of how deep they are in a multi-dimensional array:
$array['key_old'] = $array['key_new'];
unset($array['key_old']);
You can do it without any loop
Like below
$tags = str_replace("url", "value", json_encode($tags));
$tags = json_decode($tags, true);
class DataHelper{
private static function __renameArrayKeysRecursive($map = [], &$array = [], $level = 0, &$storage = []) {
foreach ($map as $old => $new) {
$old = preg_replace('/([\.]{1}+)$/', '', trim($old));
if ($new) {
if (!is_array($new)) {
$array[$new] = $array[$old];
$storage[$level][$old] = $new;
unset($array[$old]);
} else {
if (isset($array[$old])) {
static::__renameArrayKeysRecursive($new, $array[$old], $level + 1, $storage);
} else if (isset($array[$storage[$level][$old]])) {
static::__renameArrayKeysRecursive($new, $array[$storage[$level][$old]], $level + 1, $storage);
}
}
}
}
}
/**
* Renames array keys. (add "." at the end of key in mapping array if you want rename multidimentional array key).
* #param type $map
* #param type $array
*/
public static function renameArrayKeys($map = [], &$array = [])
{
$storage = [];
static::__renameArrayKeysRecursive($map, $array, 0, $storage);
unset($storage);
}
}
Use:
DataHelper::renameArrayKeys([
'a' => 'b',
'abc.' => [
'abcd' => 'dcba'
]
], $yourArray);
It is from duplicated question
$json = '[
{"product_id":"63","product_batch":"BAtch1","product_quantity":"50","product_price":"200","discount":"0","net_price":"20000"},
{"product_id":"67","product_batch":"Batch2","product_quantity":"50","product_price":"200","discount":"0","net_price":"20000"}
]';
$array = json_decode($json, true);
$out = array_map(function ($product) {
return array_merge([
'price' => $product['product_price'],
'quantity' => $product['product_quantity'],
], array_flip(array_filter(array_flip($product), function ($value) {
return $value != 'product_price' && $value != 'product_quantity';
})));
}, $array);
var_dump($out);
https://repl.it/#Piterden/Replace-keys-in-array
This is how I rename keys, especially with data that has been uploaded in a spreadsheet:
function changeKeys($array, $new_keys) {
$newArray = [];
foreach($array as $row) {
$oldKeys = array_keys($row);
$indexedRow = [];
foreach($new_keys as $index => $newKey)
$indexedRow[$newKey] = isset($oldKeys[$index]) ? $row[$oldKeys[$index]] : '';
$newArray[] = $indexedRow;
}
return $newArray;
}
Based on the great solution provided by Alex, I created a little more flexible solution based on a scenario I was dealing with. So now you can use the same function for multiple arrays with different numbers of nested key pairs, you just need to pass in an array of key names to use as replacements.
$data_arr = [
0 => ['46894', 'SS'],
1 => ['46855', 'AZ'],
];
function renameKeys(&$data_arr, $columnNames) {
// change key names to be easier to work with.
$data_arr = array_map(function($tag) use( $columnNames) {
$tempArray = [];
$foreachindex = 0;
foreach ($tag as $key => $item) {
$tempArray[$columnNames[$foreachindex]] = $item;
$foreachindex++;
}
return $tempArray;
}, $data_arr);
}
renameKeys($data_arr, ["STRATEGY_ID","DATA_SOURCE"]);
this work perfectly for me
$some_options = array();;
if( !empty( $some_options ) ) {
foreach( $some_options as $theme_options_key => $theme_options_value ) {
if (strpos( $theme_options_key,'abc') !== false) { //first we check if the value contain
$theme_options_new_key = str_replace( 'abc', 'xyz', $theme_options_key ); //if yes, we simply replace
unset( $some_options[$theme_options_key] );
$some_options[$theme_options_new_key] = $theme_options_value;
}
}
}
return $some_options;

cleaner way to pull dynamic nested elements in multidimensional array for sorting

Is there a cleaner way to extract a nested value from a 3 level deep multidimensional array, where i want to pull the result stacked inside the 3rd level, hwoever, i want to keep this dynamic so i can also grab elem from 2nd or 4th level by using an array as a parameter to determine this.
what im trying to do in the end is SORT using this element, but i cant find a way to conveniently indicate the element chain except for this way which i had to create myself:
public function keyBySubElement($nestedArray, array $subElemStack){
//essentially the loop below is doing this, but it is dynamic now so a user can specify different nested levels in the $subElemStack param.
//$nestedValue = $nestedArray[$subElemStack[0]][$subElemStack[1]];
foreach($subElemStack as $nestedElement){
if(isset($nestedValue) && is_array($nestedValue))
{
$nestedValue = $nestedValue[$nestedElement];
}
else
{
$nestedValue = $nestedArray[$nestedElement];
}
}
return $nestedValue;
}
e.g. to use this method:
assume the following data
$searchResults = array(
0 => array(
'title' => 'one',
array(
'ratings' => array(
'count' => '1'
)
)
),
1 => array(
'title' => 'two',
array(
'ratings' => array(
'count' => '5'
)
)
),
2 => array(
'title' => 'three',
array(
'ratings' => array(
'count' => '2'
)
)
),
);
foreach($searchResults as $k => $v){
$count = $this->keyBySubElement($v, array('ratings','count'));
$sortData[$k] = $count;
}
this gives me something like this
array(4) {
[0]=>
int(1)
[1]=>
int(5)
[2]=>
int(2)
}
now that i have access to my sub-sub elements value, tied in with its top level parent key, i can use it to sort the top level array by key using my new array $sortData as the reference key which can be reordered by the sub elements value that i want to sort with. i was next going just re-sort the original array by the new key values or something.
i saw a couple potential good examples, but i wasn't able to make them work. those examples are as follows:
[PHP sort: user function][1]
e.g. 1)
http://php.net/manual/en/function.sort.php#99419
e.g. 2)
Sort php multidimensional array by sub-value
e.g. 3)
/**
* Sort a 2 dimensional array based on 1 or more indexes.
*
* msort() can be used to sort a rowset like array on one or more
* 'headers' (keys in the 2th array).
*
* #param array $array The array to sort.
* #param string|array $key The index(es) to sort the array on.
* #param int $sort_flags The optional parameter to modify the sorting
* behavior. This parameter does not work when
* supplying an array in the $key parameter.
*
* #return array The sorted array.
*/
public function msort($array, $key, $sort_flags = SORT_REGULAR) {
if (is_array($array) && count($array) > 0) {
if (!empty($key)) {
$mapping = array();
foreach ($array as $k => $v) {
$sort_key = '';
if (!is_array($key)) {
$sort_key = $v[$key];
} else {
// #TODO This should be fixed, now it will be sorted as string
foreach ($key as $key_key) {
$sort_key .= $v[$key_key];
}
$sort_flags = SORT_STRING;
}
$mapping[$k] = $sort_key;
}
asort($mapping, $sort_flags);
$sorted = array();
foreach ($mapping as $k => $v) {
$sorted[] = $array[$k];
}
return $sorted;
}
}
return $array;
}
e.g. 4)
/**
* #param $array
* #param $cols
* #return array
*/
public function array_msort($array, $cols)
{
$colarr = array();
foreach ($cols as $col
=> $order) {
$colarr[$col] = array();
foreach ($array as $k => $row) {
$colarr[$col]['_'.$k] = strtolower($row[$col]);
}
}
$eval = 'array_multisort(';
foreach ($cols as $col => $order) {
$eval .= '$colarr[\''.$col.'\'],'.$order.',';
}
$eval = substr($eval,0,-1).');';
eval($eval);
$ret = array();
foreach ($colarr as $col => $arr) {
foreach ($arr as $k => $v) {
$k = substr($k,1);
if (!isset($ret[$k])) $ret[$k] = $array[$k];
$ret[$k][$col] = $array[$k][$col];
}
}
return $ret;
}
Since the data structure that they return is rather ugly and not condusive to sorting, my first move would be to reformat it into something that can be easily sorted. For example:
# create a new key, 'ratings', and put the contents of [0][ratings][count] in it
foreach ($searchResults as &$s) {
print_r($s);
# you could use your keybysubelement function to retrieve the value here
# rather than hardcoding it
$s['ratings'] = $s[0]['ratings']['count'];
unset($s[0]);
}
print_r($searchResults);
resulting data structure:
Array
(
[0] => Array
(
[title] => one
[ratings] => 1
)
[1] => Array
(
[title] => two
[ratings] => 5
)
[2] => Array
(
[title] => three
[ratings] => 2
)
)
It's then easy to create a sort function that will operate on this array to sort it according to the value in 'ratings':
# create a closure that will sort by a given key and in a given direction
# by default the order is ascending
function by_key($key, $dir = 'asc') {
return function ($a, $b) use ($key, $dir) {
if ($a[$key] > $b[$key]) {
if ($dir === 'asc')
return 1;
return -1;
}
elseif ($a[$key] < $b[$key]) {
if ($dir === 'asc')
return -1;
return 1;
}
return 0;
};
}
# sort by ratings, descending, using uasort and the custom search function:
uasort( $searchResults, by_key('ratings','desc') );
# print the results
foreach ($searchResults as $i) {
echo $i['title'] . ', ' . $i['ratings'] . PHP_EOL;
}
array order after sort:
two, 5
three, 2
one, 1
Sort by title:
uasort( $searchResults, by_key('title') );
Output:
one, 1
three, 2
two, 5

Convert dot syntax like "this.that.other" to multi-dimensional array in PHP

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);

Categories