Convert Dot Syntax Like "This.That.Other" to Multi-Dimensional Array in PHP

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

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.

PHP - Convert multidimensional array to 2D array with dot notation keys

teh codez

$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($ritit as $leafValue) {
$keys = array();
foreach (range(0, $ritit->getDepth()) as $depth) {
$keys[] = $ritit->getSubIterator($depth)->key();
}
$result[ join('.', $keys) ] = $leafValue;
}

output

Array
(
[key1] => value1
[key2.subkey] => subkeyval
[key3] => value3
[key4.subkey4.subsubkey4] => subsubkeyval4
[key4.subkey4.subsubkey5] => subsubkeyval5
[key4.subkey5] => subkeyval5
)

demo: http://codepad.org/YiygqxTM

I need to go, but if you need an explanation of that tomorrow, ask me.

How to expand a dot notation array into a full multi-dimensional array

In Laravel 6+ you can use Arr::set() for this:

The Arr::set method sets a value within a deeply nested array using "dot" notation:

    use Illuminate\Support\Arr;


$multiDimensionalArray = [];

foreach ($dotNotationArray as $key => $value) {
Arr::set($multiDimensionalArray , $key, $value);
}

dump($multiDimensionalArray);

If you are using Laravel 5.x you can use the array_set() instead, which is functionally identical.

Explanation:

Arr::set() sets value for a key in dot notation format to a specified key and outputs an array like ['products' => ['desk' => ['price' => 200]]]. so you can loop over your array keys to get a multidimensional array.

Parse dot-delimited strings and make a multidimensional array

Typically, I'd point you toward Convert dot syntax like "this.that.other" to multi-dimensional array in PHP and hammer this question as an under-researched duplicate, but you seem to have sufficient deviation in your desired output.

Your sample input and desired output are not well expressed in your question, but if I am reverse engineering your requirements correctly, you just need to explode the name values, do some conditional preparation, then push the values into the 3-level output array.

Code: (Demo)

$result = [];
foreach ($array as ['name' => $name, 'value' => $value]) {
$parts = explode('.', $name);
$parentKey = $parts[0] . 's';
$childKey = implode(array_splice($parts, 0, ctype_digit($parts[1]) ? 2 : 1));
$grandchildKey = implode('.', $parts); // $parts was reduced by array_splice()
if ($grandchildKey !== 'name') {
$result[$parentKey][$childKey][$grandchildKey] = $value;
}
}
var_export($result);

Output:

array (
'blocks' =>
array (
'block0' =>
array (
'backingIndex' => 2,
'rd.reqs' => 248907,
'rd.bytes' => 9842014208,
'rd.times' => 372870570891,
),
'block1' =>
array (
'backingIndex' => 30,
'rd.reqs' => 2871,
'rd.bytes' => 9677156,
'rd.times' => 620637479,
),
'block2' =>
array (
'backingIndex' => 30,
'rd.reqs' => 2871,
'rd.bytes' => 9677156,
'rd.times' => 620637479,
),
),
'vcpus' =>
array (
'vcpu0' =>
array (
'state' => 1,
'time' => 963654400000000,
'wait' => 0,
),
'vcpu1' =>
array (
'state' => 1,
'time' => 936409070000000,
'wait' => 0,
),
'vcpu2' =>
array (
'state' => 1,
'time' => 943396180000000,
'wait' => 0,
),
'vcpu3' =>
array (
'state' => 1,
'time' => 959496330000000,
'wait' => 0,
),
),
'balloons' =>
array (
'balloon' =>
array (
'current' => 16777216,
'maximum' => 34534530,
'swap_in' => 0,
'swap_out' => 0,
'major_fault' => 262,
'minor_fault' => 132293,
'unused' => 16153712,
'available' => 16396312,
),
),
)

Is there a way to dynamically access multidimensional PHP array?

In your case you will need something like this:

<?php


function getArray($i, $j, $k, $array) {
if (isset($array[$i]['children'][$j]['children'][$k]['country']['city']))
{
return $array[$i]['children'][$j]['children'][$k]['country']['city'];
}
}

$array[0]['children'][0]['children'][0]['country']['city'] = "some value";
$array[0]['children'][0]['children'][1]['country']['city'] = "another";
$array[0]['children'][1]['children'][1]['country']['city'] = "another one";
.
.
.
etc


echo getArray(0,0,0, $array) . "\n"; // output -> "some value"
echo getArray(0,0,1, $array) . "\n"; // output -> "another"
echo getArray(0,1,1, $array) . "\n"; // output -> "another one"

Another thing to keep in mind is that you have called the function passing only one parameter. And your multidimensional array needs at least three.

getArrayValue('1,0,2')

You have to take into account that you have called the function passing only one parameter. Even if there were commas. But it's actually a string.

getArrayValue(1,0,2) //not getArrayValue('1,0,2') == string 1,0,2

If you want to pass two values, you would have to put at least one if to control what you want the function to execute in that case. Something like:

  function getArray($i, $j, $k, $array) {
if($k==null){
if(isset($array[$i]['children'][$j]['country']['city'])){
return $array[$i]['children'][$j]['country']['city']; //
}
} else {
if(isset($array[%i]['children'][$j]['children'][$k]['country']['city'])){
return $array[$i]['children'][$j]['children'][$k]['country']['city'];
}
}
}

getArray(0,0,null, $array)
getArray(0,0,1, $array)

For the last question you can get by using the eval() function. But I think it's not a very good idea. At least not recommended. Example:

echo ' someString ' . eval( 'echo $var = 15;' );

You can see the documentation: https://www.php.net/manual/es/function.eval.php

edit:
I forgot to mention that you can also use default arguments. Like here.

<?php

$array[0]['children'][0]['children'][0]['country']['city'] = "some value";
$array[0]['children'][0]['children'][1]['country']['city'] = "another";
$array[0]['children'][1]['children'][1]['country']['city'] = "another one";

function getArray($array,$i, $j, $k = null) {
if($k==null){
echo 'getArray called without $k argument';
echo "\n";
}
else{
echo 'getArray called with $k argument';
echo "\n";
}

}

getArray($array,0,0); //here you call the function with only 3 arguments, instead of 4
getArray($array,0,0,1);

In that case $k is optional. If you omit it, the default value will be null. Also you have to take into account that A function may define default values for arguments using syntax similar to assigning a variable. The default is used only when the parameter is not specified; in particular, note that passing null does not assign the default value.

<?php
function makecoffee($type = "cappuccino"){
return "Making a cup of $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("espresso");

The above example will output:

Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.

You can read more about that here: https://www.php.net/manual/en/functions.arguments.php

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

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.

Convert delimited keys into new (multi-level) key paths

Solution is to check content of joining, not only is_array() checking.

Converted to simple function(without class/static and etc):

<?php
// worked with any lvl
function unFlatArr($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;
}

$array = ['a_a' => '2', 'a_b' => '33', 'a_b_c' => '444', 'a_b_d' => '555', 'c_c' => '12'];
var_export(unFlatArr($array));

Worked correctly, thanx Ilya, Erkin!

And another solution by mickmackusa

did not work correctly with 2+ level of input array !

Convert a dynamic multidimensional array into dot notation

I figure it out and make some starting point:

// Enum class for field types
final class InputTypeEnum {
const STRING_TYPE = 1;
const INTEGER_TYPE = 2;
const DECIMAL_TYPE = 3;
const DATE_TYPE = 4;
const DATE_EMAIL = 7;
}

// Here is class to flat the rules.

class RulesFlattener {

const ATTRIBUTE_KEY = 'attribute_key';
const CHILD_KEY = 'children';

private $input;
private $output = [];

// This array keeps map to translate rules
private $availableRules = [
'is_required' => 'required',
'input_type_id' => [
InputTypeEnum::STRING_TYPE => 'string',
InputTypeEnum::INTEGER_TYPE => 'integer',
InputTypeEnum::DECIMAL_TYPE => 'numeric',
InputTypeEnum::DATE_TYPE => 'date',
InputTypeEnum::DATE_EMAIL => 'email',
]
];

public function __construct($input) {
$this->input = $input;
}

private function extractRules($row) {
$rules = [];

foreach($row as $k => $v) {
if(isset($this->availableRules[$k])) {

$mappedRule = $this->availableRules[$k];

if(is_array($mappedRule)) {
$rule = $mappedRule[$v] ?? null;
} else {
$rule = $mappedRule;
}

$rules[] = $rule;
}
}

return array_unique($rules);
}

public function parse() {
return $this->parseRow($this->input);
}

private function parseRow($input, $parentKey = null) {
$output = [];
foreach ($input as $row) {

// Keep name of current attribute (for recursion)
$currentAttribute = $row[self::ATTRIBUTE_KEY] ?? null;

// If you want get more nested rules like product.*.photos.*.url use this:
// $currentAttribute = ( $parentKey ? $parentKey.'.*.':'') . $row[self::ATTRIBUTE_KEY] ?? null;

foreach($row as $k => $v) {
switch($k) {

case self::ATTRIBUTE_KEY:
$rules = $this->extractRules($row);
$output[($parentKey?$parentKey.'.*.':'').$v] = implode('|', $rules);
break;

case self::CHILD_KEY:
$output = array_merge($output, $this->parseRow($row[$k], $currentAttribute));
break;

}
}
}
return $output;
}

}


Now You can use it as:

$dataIn = [
[
"id" => 36,
"attribute_key" => "amount",
"attribute_value" => "Amount",
"input_type_id" => 3,
"is_required" => 1,
"parent_id" => null,
],
[
"id" => 37,
"attribute_key" => "products",
"attribute_value" => "Products",
"input_type_id" => 7,
"is_required" => 1,
"parent_id" => null,
"event" => null,
"children" => [
[
"id" => 38,
"attribute_key" => "product_name",
"attribute_value" => "Product Name",
"input_type_id" => 1,
"is_required" => 1,
"parent_id" => 37,
],
[
"id" => 39,
"attribute_key" => "price",
"attribute_value" => "Price",
"input_type_id" => 3,
"is_required" => 1,
"parent_id" => 37,
]
]
]
];

$flat = new RulesFlattener($dataIn);

$rules = $flat->parse();

and I get this output:

Array
(
[amount] => numeric|required
[products] => email|required
[products.*.product_name] => string|required
[products.*.price] => numeric|required
)


Related Topics



Leave a reply



Submit