Multi-imensional Array based on Keys - php

Is there a simple way to take a single-dimensional array and convert it to a multi-dimensional array based on the spaces, or any character(s), in the keys?
$arr['foo1'] = 'bar1';
$arr['foo2'] = 'bar2';
$arr['foo3 tier1' ] = 'bar3';
$arr['foo4 tier1' ] = 'bar4';
and turn it into
$arr['foo1'] = 'bar1';
$arr['foo2'] = 'bar2';
$arr['foo3']['tier1'] = 'bar3';
$arr['foo4']['tier1'] = 'bar4';

you can always do some sort of foreach loop
$newarr = array();
foreach ($arr as $key => $value) {
$output = explode(' ',$key,2);
if(count($output)) {
$newarr[$output[0]][$output[1]] = $value;
} else {
$newarr[$key] = $value;
}
}
That code will only work for one space but you can expand it to multiple spaces, something like
function pivot($arr,$delimeter) {
$return = array();
foreach ($arr as $key => $value) {
$output = explode($delimeter,$key,2);
if(count($output)) {
if(strpos($output[1],$delimeter) > 0) {
$return[$output[0]] = pivot(array($output[1]=>$value),$delimeter);
} else {
$return[$output[0]][$output[1]] = $value;
}
} else {
$return[$key] = $value;
}
}
return $return;
}

Related

PHP How to merge array element with next while maintaining order?

$array = ['coke.','fanta.','chocolate.'];
foreach ($array as $key => $value) {
if (strlen($value)<6) {
$new[] = $value." ".$array[$key+1];
} else {
$new[] = $value;
}
}
This code doesn't have the desired effect, in fact it doesn't work at all. What I want to do is if an array element has string length less than 5, join it with the next element. So in this case the array should turn into this:
$array = ['coke. fanta.','chocolate.'];
$array = ['coke.','fanta.','chocolate.', 'candy'];
$new = [];
reset($array); // ensure internal pointer is at start
do{
$val = current($array); // capture current value
if(strlen($val)>=6):
$new[] = $val; // long string; add to $new
// short string. Concatenate with next value
// (note this moves array pointer forward)
else:
$nextVal = next($array) ? : '';
$new[] = trim($val . ' ' . $nextVal);
endif;
}while(next($array));
print_r($new); // what you want
Live demo
With array_reduce:
$array = ['coke.', 'fanta.', 'chocolate.', 'a.', 'b.', 'c.', 'd.'];
$result = array_reduce($array, function($c, $i) {
if ( strlen(end($c)) < 6 )
$c[key($c)] .= empty(current($c)) ? $i : " $i";
else
$c[] = $i;
return $c;
}, ['']);
print_r($result);
demo
<pre>
$array = ['coke.','fanta.','chocolate.'];
print_r($array);
echo "<pre>";
$next_merge = "";
foreach ($array as $key => $value) {
if($next_merge == $value){
continue;
}
if (strlen($value)<6) {
$new[] = $value." ".$array[$key+1];
$next_merge = $array[$key+1];
} else {
$new[] = $value;
}
}
print_r($new);
</pre>
Updated Code after adding pop after chocolate.
<pre>
$array = ['coke.','fanta.','chocolate.','pop'];
print_r($array);
echo "<br>";
$next_merge = "";
foreach ($array as $key => $value) {
if($next_merge == $value){
continue;
}
if (strlen($value)<6 && !empty($array[$key+1])) {
$new[] = $value." ".$array[$key+1];
$next_merge = $array[$key+1];
} else {
$new[] = $value;
}
}
print_r($new);
<pre>
You need to skip the iteration for the values that you have already added.
$array = ['coke.', 'fanta.', 'chocolate.'];
$cont = false;
foreach ($array as $key => $value) {
if ($cont) {
$cont = false;
continue;
}
if (strlen($value) < 6 && isset($array[$key+1])) {
$new[] = $value.' '.$array[$key+1];
$cont = true;
}
else {
$new[] = $value;
}
}
print_r($new);

merge many array based on a common key php

I wasn't so clear in my first question, so i deleted it and here is a reformulation;
I have those arrays:
$open = array(array("FAI1","34"),array("FAI2","34"),array("FAI3","34"));
$click = array(array("FAI2","52"),array("FAI1","68"),array("FAI3","99"));
$unsubscribe = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
$def_sent = array(array("FAI1","34",24),array("FAI2","34",23),array("FAI3","34",27));
$SB = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
$HB = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
I searched for a function to merge them and get a result like this:
$result = array(array("FAI1",34,68,102,34,24,102,102)
,array("FAI2","34",23.....),
array("FAI3","34",27....));
and to do this, i used the function, in the php online documentation, and this is the function
function array_merge_recursive() {
$arrays = func_get_args();
$base = array_shift($arrays);
foreach ($arrays as $array) {
reset($base);
while (list($key, $value) = #each($array)) {
if (is_array($value) && #is_array($base[$key])) {
$base[$key] = array_merge_recursive($base[$key], $value);
} else {
$base[$key] = $value;
}
}
}
return $base;
}
But instead of getting the result above i got this:
FAI1|34
FAI2|34
FAI3|34
FAI2|52
FAI1|68
FAI3|99
...
So i need some help to reformulate this function to get the expected result.
Try this function:
function array_merge_rec() {
$arrays = func_get_args();
$result = array();
foreach ($arrays as $arg) {
if (is_array($arg)) {
foreach ($arg as $item) {
if (!isset($result[$item[0]])) {
$result[$item[0]] = $item;
} else {
$result[$item[0]][] = $item[1];
}
}
} else {
echo "$arg skippend because it isn't array\n";
}
}
return array_values($result);
}
Does it help?

PHP Flatten Array with multiple leaf nodes

What is the best way to flatten an array with multiple leaf nodes so that each full path to leaf is a distinct return?
array("Object"=>array("Properties"=>array(1, 2)));
to yield
Object.Properties.1
Object.Properties.2
I'm able to flatten to Object.Properties.1 but 2 does not get processed with recursive function:
function flattenArray($prefix, $array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
$result = array_merge($result, flattenArray($prefix . $key . '.', $value));
else
$result[$prefix . $key] = $value;
}
return $result;
}
I presume top down will not work when anticipating multiple leaf nodes, so either need some type of bottom up processing or a way to copy array for each leaf and process (althought that seems completely inefficient)
function flatten(array $data, $separator = '.') {
$result = array();
$stack = array();
$path = null;
reset($data);
while (!empty($data)) {
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element)) {
if (!empty($data)) {
$stack[] = array($data, $path);
}
$data = $element;
$path .= $key . $separator;
} else {
$result[$path . $key] = $element;
}
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
}
}
return $result;
}
var_dump(flatten(array("Object"=>array("Properties"=>array(1, 2)))));
Output:
array(2) {
["Object.Properties.0"]=>
int(1)
["Object.Properties.1"]=>
int(2)
}
Use function flatMapAssoc() from Kdyby Framework:
$flattened= array();
flatMapAssoc($array, function ($value, $keys) use (&$flattened) {
$flattened[implode('.', $keys)] = $value;
});
/**
* #param array|\Traversable $array
* #param callable $callback
* #return array
*/
function flatMapAssoc($array, $callback)
{
$callback = callback($callback);
$result = array();
$walker = function ($array, $keys = array()) use (&$walker, &$result, $callback) {
foreach ($array as $key => $value) {
$currentKeys = $keys + array(count($keys) => $key);
if (is_array($value)) {
$walker($value, $currentKeys);
continue;
}
$result[] = $callback($value, $currentKeys);
}
return $result;
};
return $walker($array);
}
I would use a wrapper function to hide implementation details (the prefix parameter)
and added an if branch to test for empty arrays. At last, in case of simple leaf you should use the $value variable and not the $key one.
$x = array("Object"=>array("Properties"=>array(1, 2), "test"=>array(), "post"));
function flatten ($array) {
return flattenArray('',$array);
}
function flattenArray($prefix, $array) {
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
if(count($value)) {
$result = array_merge($result, flattenArray($prefix."$key.", $value));
} else {
$result[] = "$prefix$key";
}
} else {
$result[] = "$prefix$value";
}
}
return $result;
}
echo join("\n", flatten($x));
If you want to mimic a tree structure, maybe you can use a different array structure. Something like this:
$y = array ("Object",
array("Properties", 1, 2),
"test",
"post"
);
and flattenArray becomes:
function flattenArray($prefix, $array) {
$result = array();
$prefix .=array_shift($array).'.';
foreach ($array as $value) {
if (is_array($value)) {
$result = array_merge($result, flattenArray($prefix, $value));
} else {
$result[] = "$prefix$value";
}
}
return $result;
}

PHP function to get recursive path keys with path

Given an array, I would like a flattened version of the array keys. Each array key would need the 'path' of the array, to that point, appended with an underscore.
An example explains this best.
$arr = array("location"=>0,"details"=>array("width"=>0,"height"=>0,"level"=>array("three"=>0)));
function answer($arr) {....}
the answer function would return this:
array("location","details_width","details_height","details_level_three");
UPDATE:
Here is the work in progress. It will accept an array and return the array keys, but with no depth:
function recursive_keys($input)
{
$output = array_keys($input);
foreach($input as $sub){
if(is_array($sub)){
$output = array_merge($output, recursive_keys($sub));
}
}
return $output;
}
$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
$results = array();
foreach ($ritit as $leafValue) {
$path = array();
foreach (range(0, $ritit->getDepth()) as $depth) {
$path[] = $ritit->getSubIterator($depth)->key();
}
$results[] = join('_', $path);
}
function recursive_keys(array $array, array $path = array()) {
$result = array();
foreach ($array as $key => $val) {
$currentPath = array_merge($path, array($key));
if (is_array($val)) {
$result = array_merge($result, recursive_keys($val, $currentPath));
} else {
$result[] = join('_', $currentPath);
}
}
return $result;
}
Demo here: http://codepad.viper-7.com/WQ3UYI

How to get values in an array this way with PHP?

From:
$arr = array(array('key1'=>'A',...),array('key1'=>'B',...));
to:
array('A','B',..);
$output = array();
foreach ($arr as $array_piece) {
$output = array_merge($output, $array_piece);
}
return array_values($output);
On the other hand, if you want the first value from each array, what you want is...
$output = array();
foreach ($arr as $array_piece) {
$output[] = array_unshift($array_piece);
}
But I'm thinking you want the first one.
Relatively simple conversion by looping:
$newArray = array()
foreach ($arr as $a) {
foreach ($a as $key => $value) {
$newArray[] = $value;
}
}
Or, perhaps more elegantly:
function flatten($concatenation, $subArray) {
return array_merge($concatenation, array_values($subArray));
}
$newArray = array_reduce($arr, "flatten", array());
John's solution is also nice.
Something like this should work
<?
$arr = array(array('key1'=>'A','key2'=>'B'),array('key1'=>'C','key2'=>'D'));
$new_array = array();
foreach ($arr as $key => $value) {
$new_array = array_merge($new_array, array_values($value));
}
var_export($new_array);
?>
If you want all the values in each array inside your main array.
function collapse($input) {
$buf = array();
if(is_array($input)) {
foreach($input as $i) $buf = array_merge($buf, collapse($i));
}
else $buf[] = $input;
return $buf;
}
Above is a modified unsplat function, which could also be used:
function unsplat($input, $delim="\t") {
$buf = array();
if(is_array($input)) {
foreach($input as $i) $buf[] = unsplat($i, $delim);
}
else $buf[] = $input;
return implode($delim, $buf);
}
$newarray = explode("\0", unsplat($oldarray, "\0"));

Categories